Notes (mostly) to Self: Preparing an RPi 3 B+ for service as a web server

via nginx blog

So, here is the list of things done to set up a pi single board computer (#SBC) in a generic way for use as a server.

    1. Flash the micro SD card with Raspbian Lite – this is basically Buster for running on .
    2. Touch ‘ssh’ to create an empty file on the /boot partition – this tells the OS on boot that it should start the SSH server daemon[man]. Now put the micro SD card into the device and boot it.
    3. SSH in to the server as the user pi, in my case ssh pi@192.168.0.XXX
    4. Run raspi-config as super user – sudo raspi-config. There are many setting options here, these are the ones I do immediately:
      1. Change user password for current user – to lock down the pi user account, which is otherwise very vulnerable. (You can also delete the pi user; check the documentation for more about that.)
      2. Network options -> Hostname – because my devices will be in a server farm, it is useful to give them easily-automatable hostnames like rpi3-000, rpi3-001, etc.
      3. Localisation options – there are two which probably need to be set/modified, unless you happen to be in the UK:
        1. Change locale – this affects the language (and local orthography) and national standard formats, like what day the week begins on and whether your region uses ‘,’ or ‘.’ to indicate decimal fractions in numbers.
        2. Change timezone – this primarily affects the translation (or lack thereof) of time to and from UTC. Computers use UTC, but will helpfully express that time in the local timezone.
        3. Change wifi country – because I do not want this device networking via wifi, I do not set the wifi country, which means wifi will continue to be disabled by default.
      4. Interfacing options -> SSH – here is where I make sure always runs.
      5. Advanced options -> Expand Filesystem – although this should already have occurred, running this now ensures the entire micro SD card’s storage is available.
      6. Update – updates the repo list and checks if raspi-config is the latest version. If so, all is good and we are done with this stage. If not, probably need to check the options again.
    5. Do a normal repo update and package upgrades – sudo apt update && time sudo apt -y upgrade. This can take a while, go refill your caffeinated beverage.
      real 3m25.828s
      user 1m40.911s
      sys 0m29.280s
    6. rpi.gpio-common is usually reported as installed but no longer necessary, and other packages may have been deprecated as well by the upgrade, so run sudo apt autoremove to clean up.

The SBC is now a clean, blank slate. It is now time to begin hardening it in simple ways. This is heavily plagiarized from Bryan Kennedy’s My First 5 Minutes On A Server article.

    1. Install fail2ban – sudo apt install fail2ban – which will attempt to at least slow down scripts trying to brute force their way into the system.
    2. Install Joe’s Own Editor, because it is obviously superior to vim – sudo apt install joe – this just happens to be my favourite cli editor, ignore this step and replace all mentions of ‘joe’ with your favourite editor (e.g. nano, vi, vim…)
    3. Create a ‘work’ user account. This is the user account I will use for regular day-to-day access to this computer, so it needs to be standard across all of the units in this class, and it needs to have generic set up so other sysadmins may eventually access the computer using the same account.
      1. sudo useradd sadmin
      2. sudo mkdir -p /home/sadmin/.ssh
      3. sudo chmod 700 /home/sadmin/.ssh
      4. sudo joe /home/sadmin/.ssh/authorized_keys
      5. sudo chmod 400 /home/sadmin/.ssh/authorized_keys
      6. sudo chown sadmin:sadmin /home/sadmin -R – once upon a time I had a one-line gist to make the dir, use scp to push my public key into the folder, change the file name to authorized_keys, mod the access of file and folder, and then change ownership. But this is more easily understood, and I have forgotten the cantrip.
      7. sudo passwd sadmin – use a password database to keep track of all the passwords. Make the passwords at least 200 entropy bits. Do not re-use passwords across machines, each needs a unique password so if one is somehow compromised hopefully it is the only one. (This is a pain in the butt, but usually I can use keepass to quickly c/p passwords.)
      8. In another window, test that I can log in to the server using the work user account + password. Test the account is not able to use sudo; we will fix that in the next section. Then exit.
    4. sudo visudo – add the work account right under the root account, giving the same permissions. Comment out the sudoers group line.
      sadmin ALL=(ALL:ALL) ALL
    5. Time to harden SSH, to make the unit less vulnerable to access if anyone does manage to get inside the LAN – sudo joe /etc/ssh/sshd_config Add the following to the bottom of the file:
      1. PermitRootLogin no
      2. PasswordAuthentication no
      3. AllowUsers sadmin@192.168.0.XXX plus any other IPs as necessary in the future.
      4. Restart SSH with these changes implemented – sudo systemctl restart ssh
    6. Add a firewall. Because of an issue with ufw in Raspbian Buster, the process is not straightforward and when this is eventually fixed upstream all units will need to have this modified.
      1. sudo apt install ufw
      2. sudo update-alternatives --set iptables /usr/sbin/iptables-legacy – this is the step which should not be necessary, but currently is. And for this to take effect, the computer must be rebooted.
      3. sudo reboot now – then SSH back into the machine.
      4. sudo ufw allow from to any port 22 – this will allow SSH only from my LAN based on my current set up; other set ups are possible.
      5. sudo ufw allow 80
      6. sudo ufw allow 443
      7. sudo ufw enable – ufw is now up and running, and will restart whenever the machine reboots.
      8. sudo ufw status numbered – shows the list of rules currently set up, and incidentally tells you if things are running correctly.
    7. Enable automated updates. There are multiple methods, this details a particularly flexible system:
      1. sudo apt install unattended-upgrades
      2. sudo joe /etc/apt/apt.conf.d/10periodic – this creates a new file which configures what will be updated and how often. Add the following four lines to this new file:
        APT::Periodic::Update-Package-Lists "1";
        APT::Periodic::Download-Upgradeable-Packages "1";
        APT::Periodic::AutocleanInterval "7";
        APT::Periodic::Unattended-Upgrade "1";
      3. Look at /etc/apt/apt.conf.d/50unattended-upgrades, which should have two uncommented lines designed to automagically determine which repos to allow for unattended updates. If you at least semi-regularly update/upgrade manually, only the security repo need be automatically updated. If you do not, you may be better off updating both.

At this point the SBC is reasonably secured from casual attacks. Security upgrades released upstream will be implemented within 24 hours, and once a week an apt cleanup is scheduled. Setting up a working e-mail system to get e-mailed notifications, log backups, would be a very good idea but is outside the scope of this particular article.

The next steps involve setting up a server. Because everything about the Raspberry Pi needs to be light on resources, I am going to install the nginx server, and a less-than-fully-featured edition of it to keep with the minimalist theme we have going here. This is drawn from the Linux Journal June 2016 At The Forge article by Reuven M. Lerner.

    1. sudo apt install nginx-light php7.3-fpm – this installs the server software, plus the fastCGI to run php.
    2. sudo systemctl start nginx – test the server is running by visiting the IP address and seeing the default index.html

Nginx should now be able, with appropriate site configurations, to serve static html or dynamic php sites. The latter requires, in addition to adding index.php to the index configuration line, a location regular expression block which includes at least the following if using php-fpm:

location ~ \.php$ {
    include snippets/fastcgi.php.conf;
    fastcgi_pass unix:/run/php/php7.3-fpm.sock;

This is not the final word in how to do this kind of setting up. Not by a long shot. There should be a way to automate all these steps using something like Ansible, or even creating an image to flash onto the micro SD cards. But, for a complete amateur, this might just get me (and you) up and running.

Next project: getting this set up to reverse-proxy to another, and then to do so with ssl.