Raspberry Pi installation

Using Pi OS (previously called Raspbian)

Abstract

In this article, I will explain how I set up my Raspberry Pi. The goal is to set up the Pi so that it can be used as a basic web server running Apache. I need installation notes anyway so why not share them? The final setup will be a server, that can be administered remotely via command line over SSH as a headless setup (runs without monitor or any input devices). In case this is not for you, better follow any of the other countless installation guidelines on the net.

Keep in mind that the Raspberry Pi is designed to be a toy rather than a serious product to rely on for a long time and it is barely suited for serious things like web servers. The software setup detailed here might be pretty good, but the hardware has some flaws that are difficult to overcome. I will point out the hardware issues as well and discuss possible improvements.

Pretty much all of the information provided here can be found somewhere on the internet, but I haven't seen any resource that offers a comprehensive guide covering everything I present here.

Basic installation

The installation starts with the minimal / lite image based on Debian Buster. This is the smallest, most basic image offered. Download the image from the Raspberry Pi foundation. The image used here as example is called 2020-08-20-raspios-buster-armhf-lite After download, verify the image:

sha256sum 2020-08-20-raspios-buster-armhf-lite.zip

Unpack the archive.

Find out the identifier of the SD card using GParted. In this example case the SD card is /dev/mmcblk0

Copy the Pi OS image to the SD card using dd:

dd if=2020-08-20-raspios-buster-armhf-lite.img of=/dev/mmcblk0 status=progress

SSH is disabled by default so unless a monitor and keyboard is available, the process is stuck here. How to enable SSH: After the image was copied to the SD card, the partitions on the SD card become mountable. Open the boot partition with a file manager and create an empty text file called "ssh". Un-mount the boot partition.

Make sure to disconnect from the internet in case port forwarding to the Pi IP address is already in place. Put the SD card into the Pi, connect it to the LAN and power it up. Look up the IP address of the Pi in the router and bind the MAC address to a fixed IP address. In this example case the IP address is 192.168.1.101.
Connect as user "pi" via SSH:
ssh pi&192.168.1.101
using password "raspberry".
Change the password:
sudo passwd pi
Do the same for user root.

Note: Installing Debian buster on normal hardware requires ssh and curl to manually install.

Update

Enable internet access again. Update the system:
apt-get update
apt-get upgrade
apt-get autoclean

Timezone

Check settings:
timedatectl status
In case the location is incorrect search for a better one:
timedatectl list-timezones | grep Europe Set the correct location:
timedatectl set-timezone Europe/Berlin

Locale

Become root and check locale:
locale
Set the correct locale using:
dpkg-reconfigure locales

Basic security setup

Remove user pi

Keeping the default username "pi" is not a good idea. How to change the username:
The idea is to create a new user (example adminuser), login as the other user and delete the pi user.
sudo adduser adminuser

Logout and login via SSH as the new user now.
exit
ssh adminuser&192.168.1.101
su
pkill -KILL -u pi
deluser --remove-home pi

Admin user

Add adminuser to group "adm" to access log files:
usermod -a -G adm adminuser

Restricted user

Sometimes it is a good idea to restrict some users rights. One option is to assign a different shell to this user. For the restricted user, assign /bin/rbash in /etc/passwd.

Disallow sudo

I find sudo a bad idea, although many others see this entirely different. In my opinion, gaining root privileges is far too easy this way. Like a door with a super secure lock where the key is always left in the lock. I prefer to log in as my any userm become root, do whatever needs to be done and then exit back to the normal user. This also is another obstacle in case an intruder manages to login as any regular user.
Check the config:
cat /etc/sudoers
Disable sudoers group privileges using:
visudo
to change:
%sudo ALL=(ALL:ALL) ALL
to:
#%sudo ALL=(ALL:ALL) ALL
Allow the adminuser specific commands without having to enter the users password (like reboot in this example):
adminuser ALL=(ALL:LL) NOPASSWD: /sbin/reboot
Note: Commands still need sudo as prefix.
Note: For running visudo, use su - to become root in order to inherit the root path

Disable SSH login for root

As root, edit the SSH config:
nano /etc/ssh/sshd_config
Un-comment:
#PubkeyAuthentication yes
to
PubkeyAuthentication yes
Just to make sure, set:
#PermitRootLogin prohibit-password
to
PermitRootLogin no
For now, leave:
PasswordAuthentication yes

SSH tweaks

I noticed that login via SSH can take a long time to complete and even fail due to timeout. This is a problem with clients that have the timeout set too short like most FTP apps for the phone.

For login via password or key, GSSAPI is not required and can be turned off. Check whether login without GSSAPI works by logging in via ssh by:
ssh -o GSSAPIAuthentication=no
In case that works, change the config file:
/etc/ssh/sshd_conf
by uncommenting:
GSSAPIAuthentication = no
Also, uncomment:
KerberosAuthentication no
This speeds up the authentication process.

Another way to speed up login is to disable DNS lookup for the client trying to connect.
Edit:
/etc/ssh/sshd_conf
by uncommenting the entry:
UseDNS no
Also, add:
options single-request-reopen
to the end of the config file:
/etc/resolv.conf

Setup firewall

Install the uncomplicated firewall:
apt-get install ufw
Allow SSH:
ufw limit ssh
Allow port 80:
ufw allow 80/tcp
Allow port 443:
ufw allow 443/tcp
Enable the firewall:
ufw enable
Check the firewall status:
ufw status

Setup fail2ban for SHH

Installation:
apt-get install fail2ban
Configuation:
cd /etc/fail2ban/
cp jail.conf jail.local
nano /etc/fail2ban/jail.local
check that /etc/fail2ban/jail.d/defaults-debian.conf has the SSH enabled
service fail2ban start
service fail2ban status

Unattended upgrades

Installation:
apt-get install unattended-upgrades
Configuration of what will be upgraded:
nano /etc/apt/apt.conf.d/50unattended-upgrades
Configuration of schedule:
nano /etc/apt/apt.conf.d/02periodic
paste this into the empty file:

APT::Periodic::Enable "1";
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "1";
APT::Periodic::Verbose "2";

Check the config:
unattended-upgrades -d

Setup local network

The network the Pi is attached to needs to forward some ports to the Pi. Ports 22, 80 and 443 need to be open to the world outside the private network. Open ports in general may be a security risk. In order to make sure that the open ports target the Pi only, those need to be routed to the Pi exclusively. Make sure to assign a dedicated IP to the Pi in the local router that is assigned to the MAC address of the Pi and forward the ports to the Pi only.

Web server installation

Set hostname

nano /etc/hostname

Apache installation

Install:
apt-get install apache2
Configure:
nano /etc/apache2/conf-enabled/security.conf
Add following entries and check there are no conflicting entries:

ServerTokens Prod
ServerSignature Off
<Directory /var/www/html>
Options -Indexes
</Directory>
TraceEnable Off
FileETag none

Set Servername:
nano /etc/apache2/apache2.conf
Add:
ServerName YOURNAME.COM
Test the configuration:
apachectl configtest
Restart server:
service apache2 restart
Install security modules:
apt-get install libapache2-mod-security2

Apache logrotate

nano /etc/logrotate.d/apache2
make sure to set inside /var/log/apache2/*.log { section:
daily
rotate 7
maxage 7
This will rotate the log every day and delete logs older than 7 days.

Apache config

Set permission of /var/www/html directory:

chown -R youradminuser html
chgrp -R www-data html
chmod -R 750 html
chmod g+s html

Set options for website directory:
nano /etc/apache2/apache2.conf
Set to:

<Directory /var/www/>
	Options -Indexes -FollowSymLinks
	AllowOverride All 
	Require all granted
</Directory>
Apache fail2ban

Enable jails for Apache:
nano /etc/fail2ban/jail.d/defaults-debian.conf
Add following example configuration to existing entries and tweak as needed:

[apache-auth]
enabled  = true

[apache-badbots]
enabled  = true

[apache-noscript]
enabled  = true

[apache-overflows]
enabled  = true

[apache-nohome]
enabled  = true

[apache-botsearch]
enabled  = true

[apache-fakegooglebot]
enabled  = true

[apache-modsecurity]
enabled  = true

[apache-shellshock]
enabled  = true

Reload:
service fail2ban reload
Check status:
fail2ban-client status

PHP installation

In case PHP is required, install:
apt-get install php
Enable PHP mail() function:
nano /etc/php/7.3/apache2/php.ini
sendmail_path = /usr/bin/msmtp -t -i

Note: SSMTP is currently unmaintained and not available in Debian buster. Use MSMTP instead.

DNS configuration

DuckDNS

As the admin user doing admin things exclusively and nothing else, do:
cd ~/
touch duckDNS.sh
nano duckDNS.sh
paste into file:

echo url="https://www.duckdns.org/update?domains=exampledomain,anotherdomain&token=exampletoken&ip=" | curl -k -o ~/duckDNS.log -K -

with the exampledomain and anotherdomain replaced by the Duck DNS domains and the exampletoken as well.
chmod 700 duckDNS.sh
crontab -e
paste into file:

*/5 * * * * ~/duckDNS.sh >/dev/null 2>&1
Namecheap

Install ddclient:
apt-get install ddclient
apt-get install libio-socket-ssl-perl
Run ddclient
Fill out the form as good as possible. It is not possible to set all options correctly.
Edit the configuration file:
nano /etc/ddclient.conf

ssl=yes
protocol=namecheap
use=web, web=dynamicdns.park-your-domain.com/getip
server=dynamicdns.park-your-domain.com
login=yourdomain.com
password='secret'
&

Test the configuration:
ddclient -query
Add to startup:
systemctl enable ddclient.service
systemctl start ddclient.service

Trobleshooting

ddclient was working fine for quite some time until it suddenly failed to update the IP:
WARNING: file /var/cache/ddclient/ddclient.cache, line 3: Invalid Value for keyword 'ip' = ''
I still don't know the root cause, but here are some helpful things:
Status:
service ddclient status
Manual update with verbose output for debug:
ddclient -daemon=0 -debug -verbose -noquiet
Removing the cache may help:
rm /var/cache/ddclient/ddclient.cache
Setting the IP and status in the cache manually may be even better. Together with manually updating the IP at namecheap, I believe this helped to resolve the issue.

HTTPS certificate configuration

apt-get install python-certbot-apache
certbot --apache
Follow the instructions of the installer.
Auto renew certificate:
crontab -e
Add:
* 3 * * 6 certbot renew && systemctl restart apache2.service

Secure SSH login keys

Keys are not only more secure than passwords, but also more convenient.
On the machine that wants to connect to the Pi, run:
ssh-keygen
Follow the instructions
Generated keys are stored in ~/.ssh
Copy the public key to the Pi:
ssh-copy-id user&yourdomain.com
Test SSH login with the key
Consider to disable password login for all users by:
nano /etc/ssh/sshd_config
changing:
PasswordAuthentication yes
to:
PasswordAuthentication no

Mail setup

SSMTP

Note: SSMTP is currently unmaintained in Debian buster.

apt-get install ssmtp
nano /etc/ssmtp/ssmtp.conf
Comment everything in the file and add:

root=yourmail@gmx.de
mailhub=mail.gmx.net:587
rewriteDomain=gmx.net
hostname=gmx.net
FromLineOverride=YES
AuthUser=yourmail@gmx.de
AuthPass=yourpassword
UseSTARTTLS=YES

Edit:
nano /etc/ssmtp/revaliases
Add (for example):

root:yourmail@gmx.de:mail.gmx.net:587
youradminuser:yourmail@gmx.de:mail.gmx.net:587
www-data:yourmail@gmx.de:mail.gmx.net:587

Change group of the configuration file:
groupadd ssmtp
chown :ssmtp /etc/ssmtp/ssmtp.conf

MSMTP

Install:
apt-get install msmtp msmtp-mta

Configure:
touch /etc/msmtprc
nano /etc/msmtprc
Example entries for GMX:

defaults
auth on
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile /var/log/msmtp/msmtp.log
account gmx
host mail.gmx.net
port 587
from yourmail@gmx.de
user yourmail@gmx.de
password secret
account default: gmx

Create empty log file named msmtp.log in /var/log/msmtp.
Change permissions:
chown msmtp:msmtp /var/log/msmtp
chown msmtp:msmtp /var/log/msmtp/msmtp.log
chmod 664 /var/log/msmtp/msmtp.log

Optional configuration

APT

In some cases it might be required to install packages from the "testing" branch. In general, this is best avoided. Nonetheless, here is the necessary configuration for APT to enable installation of certain packages and their dependencies from release "bullseye", which is currently in testing.

Edit:
/etc/apt/sources.list
Add:
deb http://raspbian.raspberrypi.org/raspbian/ bullseye main contrib non-free rpi

Create a file named "preferences" in /etc/apt/preferences.d
File content:

Package: *
Pin: release a=buster
Pin-Priority: 999

Package: *
Pin: release a=bullseye
Pin-Priority: 100

Create file:
/etc/apt/apt.conf.d/00local
Insert:
APT::Default-Release "buster";

Test that the new configuration works by doing an update, do an upgrade and see whether a dist-upgrade would transition to bullseye. If a dist-upgrade would upgrade more packages than an upgrade, the configuration is wrong.

If the new configuration works, packages from bullseye repository can be installed by:
apt-get install -t bullseye packagename

Backup and restore

When the SD card of my Pi died, the last backup was over a year old. I had to figure out again how to restore everything I set up in the meantime. This was the moment when I started writing this article. I create frequent backups of my data, but was too lazy to back-up my Pi. The effort doing a backup of the Pi would have been much lower.

Backup

The most easy way to create a backup is to shut down the Pi, remove the SD card, put it into an adapter and create an image of the entire SD card with dd:

dd if=/dev/mmcblk0 of=~/raspberry_backup.img status=progress

In this example, /dev/mmcblk0 is the SD card to back up.

The downside of this method is that the resulting image can be very big. Compressing the image as an archive helps to reduce file size, but the inherent problem is that deleted files will be backed up as well since the data is still there. In order to clear the unused space, the root partition could be mounted and the unused space filled with zeros. However, this will stress the SD card a lot and therefore gradually degrade the SD card.

Restore

Restoring the card image using dd works exactly the other way round with "if" and "of" reversed.

Network issues

For some reason unknown to me, the Pi randomly drops the network connection. A possible fix for this could be to periodically check whether the interface is functional and reboot in case it is not.

Install watchdog and after installation, edit the configuration:
nano /etc/watchdog.conf
Add following entries:

interface = eth0
file = /var/log/messages
interval = 600
ping = 8.8.8.8

start the watchdog:
watchdog -fsb

However, this didn't help to resolve the issue. I tend to believe that the Raspberry Pi sometimes hangs during boot and this might be related to the SSD being powered from the USB. The whole power distribution network of the Raspberry Pi is ill-designed and this may cause brownout conditions of the USB power, leaving the system in a non-functional state at boot.

Hardware improvement

Storage

The Pi runs from a SD card. SD cards are super small and cheap, but not designed for continuous use and many write cycles. They will eventually die some day, regardless of brand and model of the card. Mine died after 17 months. After a few months continuous use, some weird errors occurred. From time to time, the Pi froze and the last resort to reset was to cycle the power in order to restart. This further contributed to corrupting files. The only cure is to get rid of the SD card entirely. During my research I found how to use any mass storage as boot device.

Storage migration

Create a backup of the SD card as described previously.
Maybe null the SSD prior to using it:
WARNING: This will permanently destroy all data on the SSD!
Identify the SSD (in this example /dev/sdc)
/sbin/hdparm --user-master m --security-set-pass NULL /dev/sdc
/sbin/hdparm --user-master m --security-erase NULL /dev/sdc
Now it should be all empty.
Restore the backup on the SSD.
Create a partition in the remaining space of the SSD.
Mount the root partition and edit /etc/fstab
Just copy the line for the root partition, increment the partition UUID and replace / with /home in the copied line
Mount the new partition as well and copy everything from the /home directory of the root partition to the new partition.
From inside the /home directory of the original root partition:
cp -p * path_to_mount_point_of_new_partition
Delete everything from the root partition that was just copied over to the new partition.
In order to avoid any issues with the SSD being not ready fast enough at boot, the config.txt, which can be found on the boot partition, can be modified by adding:
program_usb_boot_timeout=1
Note that now booting from the SSD works with revision 3 B+ of the Pi board. For other Pi HW revisions, additional work is required.

Power supply

A little know fact is that the Pi requires a power supply delivering a voltage of 5.1V. The Pi is very sensitive to brown out conditions and complains about under-voltage in case the supply drops below 4.65V. When I bought my Pi, I didn't know about this and decided to use a hefty 5A capable phone charger and connected it to the Pi using a short, massive high power rated USB cable. I didn't notice that the Pi complains about under-voltage sometimes. When I measured the voltage one day, it was 4.8V. Likely the voltage dips lower in case of temporary high current demand. So probably the official power supply is a better option.

For a device that can draw up to 2.5A, I find the on-board supply decoupling a bit weak and experimented with soldering a 1000µF capacitor parallel to the 5V DC input overvoltage protection diode. Maybe this helps to buffer high current demand a bit. Most failures I've observed during my professional career and my own projects had to do with the power supply.

I don't own the original Raspberry Pi power supply, but wonder whether the long cable is a good idea. Unless the voltage is sensed at the USB connector powering the Raspberry Pi, the long cable will cause some voltage drop. From the pictures it seems that only two wires run to the USB connector and therefore, cable voltage drop is not sensed and thus not compensated other than feeding 5.1V into the cable. However, cable voltage drop is insignificant compared to voltage drop caused by the fuses on the Raspberry Pi board.

There is a polyfuse on the PCB right after the USB power inlet and this may have significant resistance (between 0.015Ω and 0.1Ω) and therefore may cause significant voltage drop in case of high current demand. Anything connected to the USB bus will further contribute to voltage drop and cause brownout of the Raspberry Pi power supply and the USB bus as well, leading to all kind of unpredictable malfunction. A much better way would be to power the Raspberry Pi from a higher voltage like 12V and step down to 5V on the Raspberry Pi PCB and implement proper USB power management in order to comply to USB interface specification. This clearly is beyond the possibilities of the small Raspberry Pi form factor. A workaround could be to connect a powered USB hub in between USB devices and the Raspberry Pi. At this point however, it might be an idea to invest into proper hardware instead without all the shortcomings specific to the Raspberry Pi unless unique features of the Raspberry Pi (like GPIO) are required.

Hardware alternatives

Somehow, was fed up with all the issues related to the Raspberry Pi platform. In case the GPIO is not needed, there are probably far more robust alternatives on the market. The main benefit of the Raspberry Pi for me is low hardware cost and low power consumption (which means low cost of ownership long term). Main drawbacks are subpar reliability and several hardware misconceptions like power management and integrity issues, no chassis options that can accommodate a 2,5" drive and interface connectors at three sides of the device. The Raspberry Pi including power supply and chassis costs around 60€ and consumes roughly 2W idle, which is hard to beat. I did some research for affordable alternative hardware options and share my conclusions here.

I was looking for a mini computer, that has low power consumption, does not feature a fan and hopefully is reliable long term. A 2,5" drive needs to fit inside so that there are no external parts apart from the power supply. Acquisition and operating cost should be as low as possible.

December 2020 I decided to move my website to the PN40-BB013M. The Raspberry Pi failed after roughly one year. At three times the cost and power consumption, the PN40 is not comparable at all. It doesn't even have the same processor architecture, let alone features and options. The ARM architecture of the Raspberry Pi would be better suited for efficient low power applications like my web server, which is idle most of the time, but on the other hand side, the Intel Celeron N4000 CPU of the PN40 has plenty of power. I'm curious how the PN40 will behave and last.

Read my review of the ASUS PN40-BB013M here.