Setting up a Raspberry Pi as a home file server

Since tastes and needs differ widely within our family, the laptops and devices at my home run Linux, Windows 10, Android and iOS. Sharing data between devices running diverse operating systems is a huge pain. We often end up using email, Dropbox or Google Drive, though it is utterly silly to move a file half way round the world to share it with somebody within arm’s reach.

This is where the Raspberry Pi comes in. It is cheap enough to be within the budget of almost anybody who runs a WiFi network at home, and it is perfectly capable of running a file server accessible from Windows, Linux, Android and iOS. This post describes how I set it up for this purpose using Arch Linux. The Raspberry Pi (RPi for short) can run many different operating systems, but I chose Arch Linux ARM because it is quite powerful and also because I run Arch Linux on my laptop.

Prerequisites

  • RPi starter kit. At a minimum, we need:
    • Raspberry Pi 3 (If you use a RPi2, you will need a USB WiFi adapter)
    • Micro USB Power Supply
    • 16 GB SD card (8 GB might be adequate) and
    • LAN cable.

      Neither a keyboard nor a display are needed as the RPi will run headless.

  • An external USB hard disk of adequate capacity (say 1TB or more) with an independent power supply. Alternatively, a powered USB hub can be used to connect a USB hard disk without independent power supply.
  • A WiFi router (administrative rights are needed)
  • A computer running Linux to setup the SD card
  • Optionally, a music system and an unused mobile phone to create a networked music system.

Overview of the key steps

Prepare SD Card

Using a linux machine with an SD card reader, I simply followed instructions at the Arch Linux ARM website.

  • Insert the SD card into a linux machine
  • Start fdisk to partition the SD card:
    • fdisk /dev/mmcblk0
  • At the fdisk prompt, delete old partitions and create a new one:
    • Type o. This will clear out any partitions on the drive.
    • Type p to list partitions. There should be no partitions left.
    • Type n, then p for primary, 1 for the first partition on the drive, press ENTER to accept the default first sector, then type +100M for the last sector.
    • Type t, then c to set the first partition to type W95 FAT32 (LBA).
    • Type n, then p for primary, 2 for the second partition on the drive, and then press ENTER twice to accept the default first and last sector.
    • Write the partition table and exit by typing w.
  • Create and mount the FAT filesystem:
    • mkfs.vfat /dev/mmcblk0p1
    • mkdir boot
    • mount /dev/mmcblk0p1 boot
  • Create and mount the ext4 filesystem:
    • mkfs.ext4 /dev/mmcblk0p2
    • mkdir root
    • mount /dev/mmcblk0p2 root
  • Download and extract the root filesystem as root:
  • Move boot files to the first partition:
    • mv root/boot/* boot
  • Unmount the two partitions:
    • umount boot root
  • Exit root
    • exit

Prepare WiFi Router

Since we are going to run the Raspberry Pi headless (with neither a display nor a keyboard), it is accessible only via ssh, and so it is essential to connect the RPi to the local network. Connecting the device to a wired network is trivial, but if the local network is a WiFi network, then we need to do some work. Obviously, the RPi cannot connect to an authenticated WiFi network without providing the password, but to type in that password, we need to be able to access the RPi, and that needs it to be on the network. The solution is to connect the RPi to the router via a wired LAN cable. To make this work, I needed to change some settings in my WiFi router (a D-Link DSL-2750U wireless router):

  • My router assumed that the cable connection is an internet connection and tried to use this as the internet gateway leading to a loss of internet connectivity. To prevent this from happening, I disabled Configure the second IP Address and Subnet Mask for LAN under Setup | Local Network | Router Settings.

  • My router also by default set up two separate isolated local networks: one wireless network and one wired network. I unchecked Enable MultiAP Isolation (under Setup | Wireless Basic) to prevent this and force the router to put wireless clients and wired clients on the same local network.

Login to the Raspberry Pi and set up WiFi connnectivity

Once this is done, we can ssh into the RPi after finding its IP address from the router’s list of DHCP clients. Suppose this IP address is 192.168.123, then we simply do:

  • ssh 192.168.1.123
  • Login as the default user alarm with the password alarm (The default root password is root)

The first order of business is to set up a WiFi connection:

  • ip link set wlan0 up
  • iw dev wlan0 connect SSID key 0:PASSWORD where SSID is the name of the WiFi network and PASSWORD is the WiFi password.
  • dhcpcd wlan0

With WiFi connectivity established, we can remove the cable connection to the router. It will never be needed again. Alternatively, you can skip the above step and remove the cable connection only after completing the more permanent setup described below:

  • Update system and install networkmanager (we will use its command line interface nmcli)
    • pacman -Syyu
    • pacman -S networkmanager
  • Connect to WiFi using nmcli so that it connects automatically every time
    • systemctl enable wpa_supplicant
    • systemctl enable NetworkManager
    • systemctl start wpa_supplicant
    • systemctl start NetworkManager
    • nmcli dev wifi connect SSID password PASSWORD where SSID is the name of the WiFi network and PASSWORD is the WiFi password.
  • Set up a static IP (say 192.168.1.123) for the Raspberry Pi by logging into the router from the laptop and adding an entry to the DHCP Reservations List (in my router it is under Setup | Local Network | Router Settings).

  • Now we are ready to restart the RPi and ssh into it again:

    su  # the default root password is `root`
    shutdown -r 0
    

    After a couple of minutes:

    ssh 192.168.1.123
    

Set up users and groups

I decided to set up user accounts for three members of the family (let me call them user1, user2 and user3) with the first two having administrative rights. I also took the opportunity to get rid of the default alarm account by renaming it as user1.

  • Create user2
    su  # the default root password is `root`
    useradd user2
    passwd user2
    su user2
    exit
    exit
    
  • Similarly for user3

  • Login as user2 and rename alarm to user1

    ssh user2@192.168.1.123
    su   # the default root password is `root`
    killall -u alarm
    usermod -l  user1 alarm
    groupmod -n user1 alarm
    usermod -d /home/user1 -m user1
    passwd user1
    
  • Grant sudo rights to user1 and user2
    nano /etc/sudoers.d/user1-user2 
    insert following lines
    ###############
    ##
    ## User privilege specification
    ##
    user1 ALL=(ALL) ALL
    user2  ALL=(ALL) ALL
    ##################
    
  • Create family group
    groupadd family
    gpasswd -a user1 family
    gpasswd -a user2 family
    gpasswd -a user3 family
    
  • Change the root password to something strong
    passwd
    

Install required packages

    sudo -i
    pacman -S --needed base-devel
    pacman -S --needed vi emacs-nox samba openssh rsync wget w3m lynx elinks
    pacman -S ntfs-3g dosfstools udisks2

Auto mount external hard disk when connected

A file server needs lots of storage capacity to hold all the files, and the RPi comes with only a small SD card for storage. The sensible thing to do was to use my largest external hard disk as the storage. It helped that this 4TB hard disk came with its own power supply because the RPi is too small to provide meaningful power supply to any of its peripherals. If you use a disk without its own power supply, you will have to connect it via a powered USB hub.

I used the method suggested by user898384 to ensure that the device would boot even if the external hard disk is not connected for some reason:

mkdir /4TB
nano /etc/fstab # add following line
/dev/sda2       /4TB    ntfs    nofail,auto,noatime,rw,user    0   0

Setup a LUKS volume on the external hard disk

  • I have discussed the need for encryption in a blog post more than a year ago and have also put up some instructions on GitHub on using cryptmount and cryptsetup for this purpose. From my laptop, I created a LUKS volume on the external hard disk and copied all the desired files to that encrypted volume.

  • I thought the system would be more usable if the RPi would mount the encrypted volume automatically. This can be done using a keyfile as explained by hyper_ch. Greater usability comes at the cost of a vulnerability if the RPi and the external hard disk were stolen simultaneously. I am willing to live with this risk because I do not plan to leave sensitive files on this volume. More sensitive files are on a different LUKS volume which is mounted only by ssh’ing into the RPi and providing a password when prompted.

    • Anyway working on my laptop, I just created a key file with 4K random bytes and added this key to the LUKS volume:
      dd if=/dev/urandom of=~/family.kf bs=1024 count=4
      sudo cryptsetup luksAddKey family.vol ~/family.kf
    • I then used scp to copy the keyfile to user1’s home folder on the Raspberry Pi
    • In the laptop, encrypt ~/family.kf or move it to an encrypted partition
    • Then ssh’ed into the RPi to move the key file from user1’s home folder to /root/keyfile and make it readable only by root.
      • In the RPi, I created a file family.service in /etc/systemd/system/ which would be run by systemd on startup:

      [Unit]
      Description=Decrypts and mounts luks volume family.vol

      [Service]
      Type=oneshot
      ExecStart=+/usr/bin/family.sh

      [Install]
      WantedBy=multi-user.target

      • The /usr/bin/family.sh script referred to in the above service file is:

      !/bin/bash

      the luks volume

      volume=/4TB/family.vol

      name of mapper

      name=family

      mount point

      mtpoint=/mnt/family

      wait for $volume to become available and then decrypt it

      for I in {1..5}
      do
      if [ -e $volume ]
      then
      cryptsetup –key-file /root/keyfile luksOpen $volume $name
      break
      else
      /bin/sleep 10
      fi
      done

      mount the decrypted volume

      mount /dev/mapper/$name $mtpoint

      • Create the mount point:

      mkdir /mnt/family

      • The service must then be enabled so that it runs automatically:

      chmod 664 /etc/systemd/system/family.service
      systemctl daemon-reload
      systemctl enable family.service

  • If for some reason, the external hard disk is connected only well after the RPi has booted, all that is needed is to ssh into it and then run

    sudo -i
    mount /4TB 
    systemctl restart family.service
    

    To disconnect and remove the external hard disk while the Raspberry Pi is running, it is necessary to run:

    sudo -i
    umount /mnt/family 
    cryptsetup luksClose family
    unmount /4TB
    

Set up samba share and permissions

  • Samba is the ideal tool for sharing files across all kinds of operating systems. The Arch Linux Wiki is an excellent resource on setting up samba.

  • First, we set up samba users and enable the daemon

    sudo -i
    systemctl enable smbd
    
    smbpasswd -a user1
    smbpasswd -a user2
    smbpasswd -a user3
    
  • We next set up the access control. A user can access a file through samba only if the user satisfies both the samba permissions and the file system permissions. The samba permissions are set up with read access for guests (anonymous login), but write access only to user1, user2 and user3. The samba share is accessible only from the local network, so even these anonymous guests would have logged in to the WiFi router. The samba configuration file /etc/samba/smb.conf is as follows:
     [global] 
        hosts allow = 192.168.1. 127.
        security = user
        map to guest = Bad User
    
     ;  guest account = nobody
    
     ; [homes]
     ;   comment = Home Directories
     ;   browseable = no
     ;   writable = yes
    
     [family]
        path = /mnt/family
        comment = General Share for my Home
        read only = yes
        guest ok = yes
        browseable = yes
        write list = user1 user2 user3
    

    At the file system level, highly granular access control can be set up. I ended up creating many different folders with different permissions inside the samba shared folder. Each of user1, user2 and user3 has a folder to which only they have read or write access. There is a folder to which the family group (user1, user2 and user3) has read and write access. And there is a public folder to which the family group has write access and the world has read access.

Make ssh more secure

I prefer to allow ssh only with keys for greater security

systemctl enable sshd.socket
sudo nano  /etc/ssh/sshd_config # add following lines

AllowGroups family
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
Banner /etc/issue

Optionally install yaourt and cryptmount

While the family volume is automatically mounted, it is useful to allow users to mount their other encrypted volumes without sudo. This requires cryptmount which is available only via yaourt and installing this in Arch Linux ARM takes some effort in editing the PKGBUILD files as explained by Ben00it.

  • Install yajl
    wget -O PKGBUILD https://git.archlinux.org/svntogit/packages.git/plain/trunk/PKGBUILD?h=packages/yajl
    nano PKGBUILD # edit following lines
         arch=('i686' 'x86_64' 'armv7' 'armv7h' 'armv7l')
         makedepends=('gcc' 'make' 'cmake')
    makepkg -Acs
    sudo pacman -U yajl-2.1.0-1-armv7h.pkg.tar.xz
    
  • Install package-query
    curl -LO https://aur.archlinux.org/cgit/aur.git/snapshot/package-query.tar.gz
    tar -xvf package-query.tar.gz
    cd package-query
    nano PKGBUILD       # edit following line
        arch=('i686' 'x86_64' 'mips64el' 'armv6h' 'armv7h' 'armv7l' 'arm' 'aarch64')
    makepkg -Acs
    sudo pacman -U package-query-*.xz
    
  • Install yaourt
    sudo nano /etc/pacman.conf # add following lines
        [archlinuxfr]
        Server = http://repo.archlinux.fr/arm
    
    sudo pacman -Syu yaourt customizepkg
    
  • Install cryptmount
    yaourt cryptmount # when prompted, edit following line in PKGBUILD
    arch=('i686' 'x86_64' 'armv7' 'armv7h' 'armv7l')
    

Optionally run a web server

There are some situations where a web server is more useful than a file server. The Raspberry Pi can easily run a light weight web server like lighttpd. It is configured with a single file lighttpd.conf which in my case is as follows:

server.port             = 80
server.bind             = "192.168.1.123"
server.username         = "http"
server.groupname        = "http"
server.document-root    = "/mnt/family/public"
server.errorlog         = "/var/log/lighttpd/error.log"
dir-listing.activate    = "enable"
index-file.names        = ( "index.html" )
mimetype.assign         = (
                                ".html" => "text/html",
                                ".txt" => "text/plain",
                                ".css" => "text/css",
                                ".js" => "application/x-javascript",
                                ".jpg" => "image/jpeg",
                                ".jpeg" => "image/jpeg",
                                ".gif" => "image/gif",
                                ".png" => "image/png",
                                ".mp3" => "audio/x-mp3",
                                ".wma" => "audio/x-ms-wma",
                                ".mkv" => "video/x-matroska",
                                ".wmv" => "video/x-ms-asf",
                                "" => "application/octet-stream"
                        )

Observe that the web server serves up files from the public folder within the samba shared folder. So guests obtain access to exactly the same set of files whether they use samba client or a web browser.

Optionally set up a networked music player

We had a music player lying around unused because nobody bothered to copy music files to a USB stick and plug that in to this player. We also had an old Android phone which was so out of date that we could not even give it away to the maid. But these two together with the file server became a networked music player. There is an android app Samba Player that does exactly what its name suggests: it plays music from a Samba share. And this app runs even on the long forgotten Android Jelly Beans that I had installed on my old phone after rooting it many years ago.

Conclusion

The Raspberry Pi is a very versatile device despite its low price. By choosing a light weight operating system (like Arch Linux) and light weight applications like lighttpd, we can get functionality that I used to think would require expensive servers. For a home network, where there are only a handful of users, the RPi is perfectly capable of taking the load. Our peak load is two family members streaming two different videos from the server, and that is a light load even for as pared down a device as the RPi.

Above all, the RPi is so small that nobody complains about it even when it is perched in the living room. Occasionally, somebody picks it up and asks what it is. They find it hard to believe the honest answer that it is just a small computer. Perhaps, I should start saying that it is a headless computer.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s