Bits and Bytes Security

Booting Raspberry PI from a NFS Root

The microSD cards, in my opinion, arent devices made to be ON all the time. For that reason, I thought if the root file system was mounted somewhere else it could be possible to turn off the microSD card, just using it for booting purposes. That was my main objective.

In fact, remote file systems are supported for a while (at least 30 years.. according to [1]), including for booting purposes, for instance using root=/dev/nfs in kernel arguments we make the root file system come from a NFS (Network File System).

Requirements

You do not need to follow exactly the steps as described here but these are the specs I’ve used in my environment.

  • To simplify, the latest raspbian Linux image can be used, however note that it uses 1.4GB just for the OS, meaning it’s a bit oversized for booting over the Internet. Building a root from stratch and adjust it to fit your needs could be the best (building the Raspbian kernel is already described in [2]).

  • A small sized SD card can be used in this case (I’d say 16MB is enough) and you don’t need to image it, in fact, I’ve used the SD card from my camera without removing the files. The size must be enough to have just the /fat partition files used in Raspbian (start.elf, cmdline.txt, kernel.bin, etc.).

  • The NFS server is necessary but it is also relatively easy to mount one (I give the instructions here for Debian distro).

We start by preparing the NFS server, then the RPI SD card.

NFS Server Preparation

The NFS Server will contain the root of the Raspbian Linux, this root contains etc, var, home and other paths including the kernel dynamic modules used. These instructions are based in [3]. In server (Debian) install the packages nfs-kernel-server and portmap.

# apt-get install nfs-kernel-server portmap

Make sure the server has network access to the RPI. It will use UDP ports to connect to this server so in case you’re using it, adjust the firewall rules for that. To configure it make sure /etc/defaults/portmap file has the OPTIONS line commented. Add ALLOW rule for portmap in hosts.allow and restart portmap.

# perl -pi -e 's/^OPTIONS/#OPTIONS/' /etc/default/portmap
# echo "portmap: 192.168.1." >> /etc/hosts.allow
# invoke-rc.d portmap restart

The portmap daemon is configured. For the NFS daemon we need to tell it to share a folder that will have RPI root file system. I’ve chosen it to be located at /rpiroot, then change NFS config.

# mkdir /rpiroot
# vim /etc/exports

Add the following line:

/rpiroot rpi_ip_address/rpi_net_mask(rw,no_root_squash,subtree_check)

If your RPI or NFS server are behind a NAT (as was my RPI..) an additional option is required: insecure so it becomes:

... (rw, insecure, no_root_squash, subtree_check) ...

You will see the following error in /var/log/syslog in those situations:

mountd[30142]: refused mount request from rpi_ip for
   /rpiroot (/rpiroot): illegal port 57913

Then refresh the NFS daemon shares with:

# export.fs -ra

To test if it’s working OK, mount the share somewhere:

# mount your_nfs_IP:/rpiroot /mnt

If any error happens here, you should fix it before continue, otherwise RPI might not be able to boot.

Now download the latest Raspbian image and mount the root partition. To do that you need to know where it starts in the image file, I’ll use parted for that.

/root/ # mkdir rpi
/root/ # cd rpi
rpi/ # wget http://...raspberrypi.img
rpi/ # parted raspberrypi.img
(parted) unit                                                             
Unit?  [compact]? B                                                       
(parted) print                                                            
Model:  (file)
	Disk /root/rpi/.....raspberrypi.img: ...B
	Sector size (logical/physical): 512B/512B
	Partition Table: msdos

	Number  Start        End          Size         Type     File sy...
	 1      1048576B     79691775B    78643200B    primary  fat16...
	 2      <u>79691776</u>B    1802502143B  1722810368B  primary  ext4...
(parted) quit
rpi/ # mkdir rpi-root
rpi/ # mount -o offset=79691776 ...raspberrypi.img rpi-root

We now create a path where we want the root to be located in the server, (remember I’ve used /rpiroot, and copy all the root files in rpi-root to there.

rpi/ # cp -varP rpi-root/* /rpiroot/
...(cp verbose output)...

We can unmount the image, but you can write down or copy_paste the start location of the FAT16 partition, we’re going to need that later. Now we’ve the NFS root ready, we just need to prepare the SD card for the RPI.

Prepare the SD Card

Mount the RPI image file you downloaded, we just need to access the first partition now. Using the start location of the FAT16 partition (1048576 in this case) do:

# mkdir rpi-img
# mount -o offset=1048576 ...raspberrypi.img rpi-fat

Plugin your SD card, if it didn’t mount it automatically, mount its FAT partition somewhere and copy the files from the image to there (replace /dev/sdb1 to the correct in your case):

# mkdir rpi-sdcard
# mount /dev/sdb1 rpi-sdcard
# cp rpi-img/* rpi-sdcard/

Now edit the cmdline.txt that should be inside the SD card and change/add the following arguments and leave the rest:

... root=/dev/nfs ip=dhcp nfsroot=your_nfs_ip:/rpiroot ...

You can configure the IP manually also, check Linux Kernel boot arguments for more info on that. The SD card and the image partitions can be unmounted now and you’re ready to test NFS root on RPI.

# umount rpi-sdcard
# umount rpi-img

Ready to Boot

Plug-in the SD card in RPI and make sure its net can reach the NFS server. I’d advice running a tcpdump at the NFS server and using a display on the RPI so you can see the console output and to better understand if something goes wrong.

Conclusions

Booting the Raspbian through a remote root file system has some advantages and drawbacks. As advantages are the unneed of imaging the SD cards and low usage of SD card. The drawbacks are that you need either a small Linux kernel or fast network connection, the disk write will be limited to 10MB/s and if you want to use swap the SD card (or USB drive?) should be used for that.

References

[1] http://www.ibm.com/developerworks/library/l-network-filesystems/index.html

[2] http://elinux.org/RPi_Kernel_Compilation

[3] http://wiki.debian.org/NFSServerSetup