HOWTO Read-only root filesystem
From Gentoo Linux Wiki
| Installation • Kernel & Hardware • Networks • Portage • Software • System • X Server • Gaming • Non-x86 • Emulators • Misc |
The aim of this HOWTO is to have a read-only root. The method presented here is not perfect, however it should work well in most cases. The information used here come from a text written by Thomas Hood for the Debian distribution.
Contents |
[edit] Aim and principles
The root filesystem doesn't have to be modified often, except for some configuration files. So it is possible to mount this directory read-only. There are several reasons:
- if the user wants a simple solution to use a read-only support for the root directory (perhaps a better solution could be to have a look at how to create one's own live CD).
- if the user is in paranoid mode and wishes to secure its system even more.
- to avoid data loss in the root partition in the event of a power failure or system crash.
- if the user wants to mount the same nfsroot on several thin clients
- if the user is using initramfs as the root filesystem
- if the user wants to run the root filesystem on a flash based device and not wear the flash out
[edit] Old Method
If you want to modify as few bootscripts as possible, skip down to the Unionfs Method section.
You need to use devfs or udev. Either of the two are standard for a while in Gentoo. Either are available or by default installed in Gentoo.
It is not possible to directly switch your root to read-only, because several important files in /etc have to be write-accessed at boot time. A first idea could be to have a separate partition for /etc with write access. However, this is not possible since /etc/fstab needs to be accessed before the partitions other than root are mounted.
Software should include in their code that /etc is for static configuration files and /var for files that can be modified. Meanwhile, you'll need to:
- modify the software code, which is simple in the case of boot scripts, but not in the case of complied software. More, you'll have to apply the patch every update of the system.
- replace some files in /etc by symbolic links pointing to a copy of that files on a partition with write access.
If the root partition has to be read-only, it must not contain directories needing a write access. Then you'll have to create a partition with write access for /home, /tmp, /var (either a separate partition for each, or only one for all using LVM or symbolic links e.g. pointing to /home/var or /home/tmp)
File in /etc that need write access have to be on a secure place. In the following, let's assume that /var/etc is created on a local disk for that use.
[edit] mtab
/etc/mtab describes the filesystems eventually mounted. It has to be written each time a filesystem is mounted or unmounted, specially at boot time. /etc/mtab is a special case because mount hasn't been designed to work with symbolic links.
Before you execute the following, you should probably first execute
# find /etc -newer /etc/mtab > /tmp/sinceboot.txt
This will write out what files have changed since the last boot. It will come in handy later.
[edit] First method
the first solution is to replace mtab by a symbolic link to /proc/mounts. This special file also describes the mounted filesystems, and can replace mtab without needing a write access on it. However, in some cases including loop devices, this may not work, because information provided by /proc/mounts are less precise than those in /etc/mtab. .
# cd /etc # rm mtab # ln -s /proc/mounts mtab
We also need to tell to the bootscripts that they should not try to write to mtab. It is as simple as adding the -n option to any mount option in /etc/init.d/localmount
In localmount, replace two lines:
| File: /etc/init.d/localmount |
|
line 13:
mount -at nocoda,nonfs,noproc,noncpfs,nosmbfs,noshm >/dev/nullbecomes: mount -nat nocoda,nonfs,noproc,noncpfs,nosmbfs,noshm >/dev/nullline 36: mount -t ${usbfs} none /proc/bus/usb &>/dev/null
becomes: mount -nt ${usbfs} none /proc/bus/usb &>/dev/null
|
(Comment: as of 1/25/2008, the script commands are slightly different and at different lines. However, the changes are still obvious and straightforward.)
[edit] Second method
Second solution, replace /etc/mtab by a symbolic link to a file on a partition with write access. To do that, we'll first have to apply a patch to mount. The patch, written for sys-apps/util-linux version 2.12, can be obtain from Thomas Hood's page.
There is an updated version of the patch that applies to util-linux-2.12q attached to bug #98403, along with an ebuild for util-linux-2.12q that applies the patch.
Let's assume that the sources of the package and the patch are on the disk. To apply the patch and recompile, type the following (modifying version numbers and directories if needed):
# ebuild /usr/portage/sys-apps/util-linux/util-linux-2.12-r5.ebuild unpack # cd /var/tmp/portage/util-linux-2.12-r5/work/util-linux-2.12/ # patch -Np1 < (/home/.../)mount-2.12-symlinkmtab_20030914 # cp /bin/mount /bin/mount.old # cp /bin/umount /bin/umount.old # ebuild /usr/portage/sys-apps/util-linux/util-linux-2.12-r5.ebuild merge
It is then possible to switch mtab to a symbolic link:
# cd /etc/ # cp mtab /var/etc # rm mtab # ln -s /var/etc/mtab
If, for any reason, mtab was not synced with what it should be, it is possible to create a usable mtab typing:
# cat /proc/mounts > /var/etc/mtab
[edit] boot scripts
Some Gentoo boot scripts in /etc/init.d need write access even before file systems are mounted (which is done by localmount). This is the case of modules, checkroot, checkfs, and hostname. In this case, the best to do is to see if they don't do something too important with their write access. the script domainname needs write access but is launched after local filesystems are mounted.
[edit] modules
the module dependencies are calculated at Gentoo boot time and need write access on root partition. To circumvent the problem, just follow this TIP (in French, for now, sorry). If you update your configuration in /etc/modprobe.d, you'll have to launch update-modules with write-access on root filesystem to update /etc/modprobe.conf.
(Comment: as of 1/25/2008, the script no longer exists at that site.)
[edit] checkroot
The script checkroot initializes mtab from /proc/mounts. If the method chosen for mtab is a link to /proc/mounts, there is nothing to be done. The system will use /proc/mounts. There is no need to initialize mtab but doing it won't hurt.
For the second method (a link to /var/etc/mtab), the initialization of mtab has to be done after the filesystems have been mounted. To do that, it is possible to include the needed code in a boot script. The simplest way is to add that code in /etc/conf.d/local.start, specially dedicated to user modifications. However, the best is to do the initialization as soon as possible, as soon as the filesystem on which mtab resides have been mounted. If this solution is preferred, then the code would be added to the function start of localmount.
The code is from /etc/init.d/checkroot, just replacing /etc/mtab by /var/etc/mtab
| File: /etc/init.d/checkroot |
#
# Create /etc/mtab
#
# Clear the existing mtab
> /var/etc/mtab
# Add the entry for / to mtab
mount -f /
# Don't list root more than once
awk '$2 != "/" {print}' /proc/mounts >> /var/etc/mtab
# Now make sure /etc/mtab have additional info (gid, etc) in there
for x in $(awk '{ print $2 }' /proc/mounts | uniq)
do
for y in $(awk '{ print $2 }' /etc/fstab)
do
if [ "${x}" = "${y}" ]
then
mount -f -o remount $x
continue
fi
done
done
|
[edit] checkfs
If the method chosen is a link to /proc/mounts, there is no problem.
If the method chosen for mtab is the link to /var/etc/mtab, then /etc/init.d/checkfs won't be able to access mtab and will complain at boot time for systems labelled in /etc/fstab as noauto. For example:
ext2fs_check_if_mount: No such file or directory while determining whether /edv/hdax is mounted
The filesystems for which it cannot guess whether they are mounted or not are precisely those which are not mounted. So the failure of check_if_mount is no problem (see e2fsprogs/e2fsck/unix.c, function check_mount).
[edit] hostname
/etc/init.d/hostname reads /etc/hostname, sets the name of the machine and write it in /etc/env.d/hostname. If write access of not possible, this information won't be updated. This is not important as long as the machine name does not change. If you decide to change the name (updating /etc/hostname), think to execute the hostname script (with write access allowed).
[edit] domainname
The script /etc/init.d/domainname is launched after the flesystems are mounted and will create resolv.conf and resolv.conf.new. It is not sufficient to create a link to /var/etc/resolv.conf, because domainname wants to rename /etc/resolv.conf.new, but mv does not follow symlinks. If you want the script to end as it should, you'll have to modify domainname. Either, you can:
- create in /etc symbolic links to /var/etc/resolv.conf and /var/etc/resolv.conf.new and replace the command mv in domainname by cp.
- replace in domainname every reference to /etc/resolv.conf and resolv.conf.new by /var/etc/resolv.conf and /var/etc/resolv.conf.new.
(Comment: as of 1/25/2008, the domainname script no longer exists in a default gentoo installation.)
[edit] Individual files
The exact list of files that need to be write accessed depends on software installed. Recall the find command that you executed earlier. You can now look at
# more sinceboot.txt
This command prints the list of files modified since the last time a filesystem was mounted.
Among the files which need write access in /etc, there are:
- adjtime, ioctl.save, resolv.conf, resolv.conf.new,
- csh.env, profile.env (which are Gentoo-specific),
- printcap, cups/certs (if you use cups for printing).
Those files are used only after the local filesystems are mounted, so we can replace them by a link to /var/etc. This has to be done for all files. In the case of adjtime, the FHS standard recommends /var/lib/hwclock as a suitable directory.
# cd /var/lib # mkdir hwclock # cp /etc/adjtime /var/lib/hwclock # cd /etc # rm adjtime # ln -s /var/lib/hwclock/adjtime
[edit] varia
It is necessary to make symlinks to redirect some directories on a location with write access. This is the case of /usr/tmp which can be redirected to /tmp or /var/tmp.
# cd /usr/ # rmdir tmp/ # ln -s /tmp/
[edit] Additional modifications
If you use services like samba, cups or ppp, you'll have to make some additional modifications.
[edit] cups
In the case of cups,, you'll have to create a symbolic link as above for the directory /etc/cups/certs. Don't forget to give right permissions. The directory must belong to lp group.
# cd /var/etc/ # mkdir cups/ # chown root:lp cups # cd cups # cp -p -r /etc/cups/certs certs # cd /etc/cups # mv certs certs.old # ln -s /var/etc/cups/certs # chown root:lp /var/etc/cups/certs/*
[edit] Unionfs Method
Unionfs allows different directories to be "stacked" into a single filesystem. Using unionfs and tmpfs, you can make all your bootscripts think that /etc is read-write.
This can be accompished in three steps.
[edit] Step 1: Preparing the kernel
As of kernel 2.6.20, unionfs is not included in vanilla-sources; but it is included in mm-sources. If you don't want to use mm-sources, then you must obtain the patch from the unionfs website
Ensure that "Virtual memory file system support (former shm fs)" (CONFIG_TMPFS) and "Union file system" (CONFIG_UNION_FS) are enabled, then compile and reboot.
[edit] Step 2: Creating the tmpfs mountpoint in /etc
# mkdir /unionfs/etc
This directory will be used to mount a small tmpfs volume where writes to /etc will be redirected. All writes will be lost after each reboot.
[edit] Step 3: Modifying /sbin/rc
This is the only bootscript file that must be modified.
Open /sbin/rc with your favorite text editor. We need to setup the unionfs mount early in the sysinit runlevel. Do a text search for "sysinit" and you should find something like this:
# First time boot stuff goes here. Note that 'sysinit' is an internal runlevel
# used to bring up local filesystems, and should not be started with /sbin/rc
# directly ...
if [[ ( ${RUNLEVEL} == "S" || ${RUNLEVEL} == "1" ) && ${argv1} = "sysinit" ]]
then
# Setup initial $PATH just in case
PATH="/bin:/sbin:/usr/bin:/usr/sbin:${PATH}"
Add the following lines after the PATH statement:
/bin/mount -n -t tmpfs tmpfs /unionfs/etc -o size=1M /bin/mount -n -t unionfs unionfs /etc -o dirs=/unionfs/etc=rw:/etc=ro echo "tmpfs /unionfs/etc tmpfs rw,size=1M 0 0" >> /etc/mtab echo "unionfs /etc unionfs rw,dirs=/unionfs/etc=rw:/etc=ro 0 0" >> /etc/mtab
[edit] Final modifications
When these modifications are done, it is necessary to place the directory for the next reboot. In the file /etc/fstab, the line containing the root directory has to be modified to add the read-only (ro) option.
| File: /etc/fstab |
|
For example, the line: /dev/hdax / ext2 noatime 0 1 becomes: /dev/hdax / ext2 noatime,ro 0 1 |
In everyday use, you'll need from time to time to write to the root directory - if you want to install new software or update the configuration, etc.
This will be the case if you need to use your /root directory: it must not be on a separate partition, since it is useful in case of severe problems to be connected as root on the system, as other filesystems are not mounted.
To switch to write access and go back to read-only, the following command typed as superuser is sufficient:
# mount -w -o remount / # Write allowed # mount -r -o remount / # Read-only
To ease that task, it is possible to write aliases for the superuser shell.
# echo "alias rw='mount -w -o remount /'" >> ~/.bashrc # echo "alias ro='mount -r -o remount /'" >> ~/.bashrc
(The bash initfile is sometimes called .bash_login)
Switch to read-only and reboot. Look carefully at the boot messages; they will warn you of any problems caused by the inability to write. Once you have resolved the problems, you can go back and add material to this tutorial.
[edit] Using write access
If you wish to remount your root filesystem with write access, remember that some programs (mount for example, if the patch from Thomas Hood is not used) will try as soon as they can to replace the symlinks by regular files. When you finish your work in write access mode, if you have launched any programs needing access to the files you have symlinked, check that your links still exist and create them again if necessary.
Before launching any emerge think to go back to write access. If you forget to do so, emerge will not be able to copy the files it has compiled. Also, for emerge --unmerge, it will not be able to remove files you asked to remove, but it may think it did, so it will incorrectly update its database of installed programs. Then you'll need to recompile the program to be able to remove it!
If you used the unionfs method, then modifying /etc requires a few extra steps. After your root filesystem is remounted read-write, unmount the unionfs mount:
# umount -n /etc
Make your changes, then restore the unionfs mount. Don't mount or unmount any filesystems until you preform the next step, as your /etc/mtab file will become out of sync with reality.
# mount -n -t unionfs unionfs /etc -o dirs=/unionfs/etc=rw:/etc=ro
[edit] Remarks
If you follow this tip, you had to modify modules and maybe localmount and domainname; or /sbin/rc. If this is the case, you'll be obliged to apply the same modifications when you'll update your system with emerge baselayout.
Some modifications are necessary in the Gentoo initscripts so that the user can choose to use its root filesystem read-only. But these modifications are not very complex, and could be integrated in the distribution (as Thomas Hood is trying to do for Debian). If some Gentoo developer reads these lines...
if you have modified mount/umount manually to apply the patch mentioned above, don't forget to do "chmod 4711 /bin/mount /bin/umount".
[edit] References
The basic information of this method are taken from a text from Thomas Hood. The document is no longer available from his website.
