Debian 10 on Raspberry Pi 4 8GB

Debian on Raspberry Pi 4 8GB

I am a big fan of Debian. I like the philosophy and the fact it is not owned by a company. With the death of CentOS I feel it becomes even more important.

I know you can get Ubuntu Server 20.04 LTS x64 for the Pi but I want Debian.

This is run as an unofficial side project at https://raspi.debian.net/

This post covers the little niggles I encountered getting it all setup and working like I wanted ready for anything else you wanted to do with your Pi.

Download the latest image file from https://raspi.debian.net/tested-images/

Flash it onto your MicroSD card.

xzcat raspi_4.img.xz | sudo dd of=/dev/{YOUR_DEVICE} bs=64k oflag=dsync status=progress

On first boot you need to login locally. The root account has no password and is therefore not enabled for remote login via SSH.

Now before you do anything else set a password for the root account.

passwd root

Initial setup

Install sudo, add a new user, set a password and add them to the sudo.

apt install sudo
useradd -m john-doe
passwd little-death
usermod -aG sudo john-doe

Change the host name and update the hosts file.

nano /etc/hostname

rpi4-debian10

Quit and save the file.

nano /etc/hosts

Add an additional line at the bottom of the file.

127.0.1.1 rpi4-debian10

Quit and save the file. Without this entry you get an error when running apt commands.

Look up your IP address.

ip a

You can now login via SSH with your new user using the IP address you just looked up.

ssh [email protected]

Once logged in you will see that the command prompt is only a $ sign. This is because the shell is /bin/sh

You can change the shell for your new user with the command.

chsh -s /bin/bash

You will be prompted for the password of the user. Once done logout of the SSH session with exit and then back in again.

Now test upgrading the system.

sudo apt update

sudo apt upgrade

Install locales. If you don’t do this you will get a warning whenever installing software.

sudo apt-get install locales-all

Static IP address issue

So I went to set a static IP as I am used to.

sudo nano /etc/network/interfaces

# interfaces(5) file used by ifup(8) and ifdown(8)
# Include files from /etc/network/interfaces.d:

source-directory /etc/network/interfaces.d

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
address 192.168.0.2
netmask 255.255.255.0
broadcast 192.168.0.255
gateway 192.168.0.1
dns-nameservers 192.168.0.4 192.168.0.6
# This is an autoconfigured IPv6 interface
iface eth0 inet6 auto

But after a reboot an ip a shows me a static and dynamic address?

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:e0:cc:dc brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.94/24 brd 192.168.0.255 scope global dynamic eth0
       valid_lft 86386sec preferred_lft 86386sec
    inet 192.168.0.2/24 brd 192.168.0.255 scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet6 2001:8b0:1741:ec2e:dea6:32ff:fee0:ccdc/64 scope global dynamic mngtmpaddr 
       valid_lft 4294965554sec preferred_lft 4294965554sec
    inet6 fe80::dea6:32ff:fee0:ccdc/64 scope link 
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether dc:a6:32:e0:cc:dd brd ff:ff:ff:ff:ff:ff

The culprit was the file /etc/network/interfaces.d/eth0

Change

auto eth0
iface eth0 inet dhcp

to

auto eth0
iface eth0 inet static

I also changed the fourth line in /etc/network/interfaces from

source-directory /etc/network/interfaces.d

to

source-directory /etc/network/interfaces.d/*

Now after a reboot all was well with the output from ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:e0:cc:dc brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 brd 192.168.0.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 2001:8b0:1741:ec2e:dea6:32ff:fee0:ccdc/64 scope global dynamic mngtmpaddr 
       valid_lft 4294966050sec preferred_lft 4294966050sec
    inet6 fe80::dea6:32ff:fee0:ccdc/64 scope link 
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether dc:a6:32:e0:cc:dd brd ff:ff:ff:ff:ff:ff

Swap

The Debian Pi image has no swap file for whatever reason. To create a small swap file enter the following commands.

Elevate to root

sudo su

Create the file.

fallocate -l 2G /swapfile

Limit the swap file to root access only.

chmod 600 /swapfile

Mark the file as a swap file.

mkswap /swapfile

Turn the swap file on.

swapon /swapfile

Edit the fstab file.

nano /etc/fstab

Add the below line to the end of the file. This will ensure the swap file is turned on after a reboot.

/swapfile none swap sw 0 0

Reboot the server.

shutdown -r now

Check that the swap is present.

free -h

              total        used        free      shared  buff/cache   available
Mem:          7.6Gi        58Mi       7.5Gi        24Mi        98Mi       7.4Gi
Swap:         2.0Gi          0B       2.0Gi

Security

SSH keys

So lets make the server more secure. First thing to do is setup SSH keys and disable password access.

Enter the following command on the client PC you are connecting from. We are assuming this is running a Linux OS.

I also do this on the Pi put it is not strictly required.

ssh-keygen -t rsa

This command creates a hidden .ssh folder in your home directory. Inside this will be a file called id_rsa.pub. Copy the contents of this file and paste it into a file called authorized_keys on the Pi in the same .ssh directory.

Logout out of the SSH session on the Pi and back in again. It should login without prompting for a password.

Now edit the ssh server config file with the following command.

sudo nano /etc/ssh/sshd_config

Change the following line

#PasswordAuthentication yes

to

PasswordAuthentication no

and reload the settings.

sudo systemctl reload sshd.service

I also like to change the SSH port to something above 64000.

The first file determines what port you connect to the Pi on.

sudo nano /etc/ssh/sshd_config

The second file will change what port you connect out from the Pi if you SSH from it to something else.

sudo nano /etc/ssh/sshd_config

In both cases find the line for Port uncomment it and change the number to whatever you want.

This does mean that you will have to remember to use the -p switch when using SSH to connect to the Pi. An example would be.

ssh -p 64000 john-doe@rpi4-debian10

nftables firewall

In Debian 10 the recommended firewall is nftables rather than iptables. I think it is easier to use personally.

Install and enable.

sudo apt install nftables

sudo systemctl enable nftables.service

A selection of templates are provided at

/usr/share/doc/nftables/examples

The active configuration file is located at /etc/nftables.conf

sudo nano /etc/nftables.conf

The file will look as below.


#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;
        }
        chain forward {
                type filter hook forward priority 0;
        }
        chain output {
                type filter hook output priority 0;
        }
}

For a basic setup copy in the version below and edit the port list as needed.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;

                # accept any localhost traffic
                iif lo accept

                # accept traffic originated from us
                ct state established,related accept

                # activate the following line to accept common local services
                tcp dport { 64000 } ct state new accept

                # accept neighbour discovery otherwise IPv6 connectivity breaks.
                ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit,  nd-router-advert, nd-neighbor-advert } accept

                # count and drop any other traffic
                counter drop
        }
}

Now start.

sudo systemctl start nftables.service

A basic probe from nmap will show nothing.

you@workstation:~$ nmap rpi4-debian10
Starting Nmap 7.80 ( https://nmap.org ) at 2020-12-13 00:17 GMT
Note: Host seems down. If it is really up, but blocking our ping probes, try -Pn
Nmap done: 1 IP address (0 hosts up) scanned in 3.03 seconds

If you expand the port range and add in the suggested switches you get the following.

you@workstation:~$ nmap -Pn -p 1-65000 rpi4-debian10
Starting Nmap 7.80 ( https://nmap.org ) at 2020-12-13 00:23 GMT
Nmap scan report for rpi4-debian10 (192.168.0.2)
Host is up (0.00035s latency).
Not shown: 64997 filtered ports
PORT      STATE  SERVICE
64000/tcp open   unknown

Nmap done: 1 IP address (1 host up) scanned in 262.56 seconds

If you want to be able to ping the Pi change your nftables.conf file as below.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;

                # accept any localhost traffic
                iif lo accept

                # accept traffic originated from us
                ct state established,related accept

                # activate the following line to accept common local services
                tcp dport { 64000 } ct state new accept

                # accept neighbour discovery otherwise IPv6 connectivity breaks.
                ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit,  nd-router-advert, nd-neighbor-advert } accept

                ip protocol icmp icmp type echo-request accept
                ip6 nexthdr icmpv6 icmpv6 type echo-request accept

                # count and drop any other traffic
                counter drop
        }
}

fail2ban

It is still useful to have fail2ban even if you have setup SSH keys as it allows you to see a log of where attacks are coming from. Also it means the program is ready for you to configure when you install additional services onto your Pi that you might want to protect.

sudo apt install fail2ban

Now make copies to the two major configuration files. These are the ones we will edit as they will not be overwritten by any future upgrade of the application.

sudo cp /etc/fail2ban/fail2ban.conf /etc/fail2ban/fail2ban.local

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

By default five incorrect login attempts within ten minutes will lead to a ten minute ban.

All we need to do to enable the protection for ssh is to edit the jail.local file.

sudo nano /etc/fail2ban/jail.local

Scroll down to the sshd file and add the line enabled = true as below. If your SSH port is non standard change the port line as well.

[sshd]
enabled = true
# To use more aggressive sshd modes set filter parameter "mode" in jail.local:
# normal (default), ddos, extra or aggressive (combines all).
# See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details.
#mode   = normal
port    = 64000
logpath = %(sshd_log)s
backend = %(sshd_backend)s

Exit and save the file. Then restart fail2ban.

sudo systemctl restart fail2ban.service

You can configure fail2ban to send email alerts to you as well if desired although this requires the installation of an mta like sendmail first.

The log files you will want to look at are

/var/log/fail2ban.log

and

/var/log/auth.log

The first shows the application running.

john-doe@rpi4-debian10:/var/log$ sudo tail -f fail2ban.log 
2020-12-13 20:57:50,205 fail2ban.jail           [11335]: INFO    Jail 'sshd' uses pyinotify {}
2020-12-13 20:57:50,213 fail2ban.jail           [11335]: INFO    Initiated 'pyinotify' backend
2020-12-13 20:57:50,215 fail2ban.filter         [11335]: INFO      maxLines: 1
2020-12-13 20:57:50,289 fail2ban.server         [11335]: INFO    Jail sshd is not a JournalFilter instance
2020-12-13 20:57:50,291 fail2ban.filter         [11335]: INFO    Added logfile: '/var/log/auth.log' (pos = 15423, hash = b7b651a6982ddc689261d6962a1ee4d735492404)
2020-12-13 20:57:50,302 fail2ban.filter         [11335]: INFO      encoding: UTF-8
2020-12-13 20:57:50,303 fail2ban.filter         [11335]: INFO      maxRetry: 5
2020-12-13 20:57:50,303 fail2ban.filter         [11335]: INFO      findtime: 600
2020-12-13 20:57:50,304 fail2ban.actions        [11335]: INFO      banTime: 600
2020-12-13 20:57:50,309 fail2ban.jail           [11335]: INFO    Jail 'sshd' started

The second shows login attempts. I attempted to connect from a different machine on 192.168.0.3 which does not have the required keys.

john-doe@rpi4-debian10:/var/log$ sudo tail -f auth.log
Dec 13 20:58:28 rpi4-debian10 sshd[11363]: Connection closed by authenticating user john-doe 192.168.0.3 port 35022 [preauth]
Dec 13 20:58:29 rpi4-debian10 sshd[11365]: Connection closed by authenticating user john-doe 192.168.0.3 port 35024 [preauth]
Dec 13 20:59:05 rpi4-debian10 sudo: john-doe : TTY=pts/0 ; PWD=/home/john-doe ; USER=root ; COMMAND=/usr/bin/nano /etc/fail2ban/jail.local
Dec 13 20:59:05 rpi4-debian10 sudo: pam_unix(sudo:session): session opened for user root by john-doe(uid=0)
Dec 13 20:59:49 rpi4-debian10 sudo: pam_unix(sudo:session): session closed for user root
Dec 13 21:01:39 rpi4-debian10 sudo: john-doe : TTY=pts/0 ; PWD=/var/log ; USER=root ; COMMAND=/usr/bin/tail -f fail2ban.log
Dec 13 21:01:39 rpi4-debian10 sudo: pam_unix(sudo:session): session opened for user root by john-doe(uid=0)
Dec 13 21:02:03 rpi4-debian10 sudo: pam_unix(sudo:session): session closed for user root
Dec 13 21:02:10 rpi4-debian10 sudo: john-doe : TTY=pts/0 ; PWD=/var/log ; USER=root ; COMMAND=/usr/bin/tail -f auth.log
Dec 13 21:02:10 rpi4-debian10 sudo: pam_unix(sudo:session): session opened for user root by john-doe(uid=0)
Dec 13 21:04:55 rpi4-debian10 sshd[11420]: Connection closed by authenticating user john-doe 192.168.0.3 port 35046 [preauth]
Dec 13 21:04:55 rpi4-debian10 sshd[11422]: Connection closed by authenticating user john-doe 192.168.0.3 port 35048 [preauth]
Dec 13 21:04:56 rpi4-debian10 sshd[11424]: Connection closed by authenticating user john-doe 192.168.0.3 port 35050 [preauth]
Dec 13 21:04:58 rpi4-debian10 sshd[11428]: Connection closed by authenticating user john-doe 192.168.0.3 port 35052 [preauth

You can see after several attempts the connect is shut down automatically by fail2ban.

6 Likes

Nice write up. I am also running the Debian Pi4 project on on my 8GiB Pi. I have found that there is nothing that I run on the Pi that requires Swap (using the Pi as a 4 disk NAS), but I have 1GiB of space reserved on the 8GiB SD card.

Funny enough, I have found that I cannot run Testing or Unstable on the Pi4 because the bpo kernel seems to not have been upstreamed into Mainline which is a Bumber as I run Unstable on my PC and on one of my ODroids.

Also Python3 GPIO does not work on the Pi4 when using the Debian build, but there is a patch that is currently being tossed around. Should land in Testing or Unstable soon.

Overall, I have had much better experience with the Pi4 than the Pi B (Non-Plus original) as mainline support has been better out of the box. I still think that Raspbian is still better suited but I wish that they would hurry up with 64Bit support or at least release a Tracker stating what they need help with so that the community could get involved. Even Ubuntu already has 64bit support and seems to work a little bit better than base Debian with the Pi specific patches.

You probably want to allow input icmp, so that don’t break mtu discovery. (especially these days with random people wrapping and re-wrapping packets in random sizes to as they tunnel your traffic between clouds)

Constant read and writes to the SD card cause a lot more wear to the SD card which already has a small lifetime compared to other media. Additionally, it’s also much slower than traditional boot media which might cause some performance degradation.
And as @Mastic_Warrior already mentioned, the Pi can do fine without it, at least for most applications for most people.

To add to your guide, it’s also worth considering migrating the root partition to a separate drive and use the SD card only as a boot-loader. In my experience, albeit on older Pis, the SD card tends to get corrupted easily over time (especially if you overclock the Pi), so keeping the data on a separate drive is a good idea. Also keeping a backup of the boot-partition for a quick recovery should be part of that strategy.

1 Like

Yes, you are correct that the Pi can do without a swap. I hadn’t really thought about the wear issue. I added one as I monitor all my machines and VMs with Zabbix. As soon as I put the agent on it leapt up saying I had no swap.

The standard Raspiberry Pi OS has a 100MB swap so I went overkill for sure.

I thought I put that in here.

Unless what you are talking about is something that would require a different line in the file.

If so please let me know.

That’s echo request (and presumably echo reply via related tracking)… aka. ping packets. I don’t know the nft “type” value for “fragmentation required”. Personally at home, i just let through all ICMP.

The practice of blocking ICMP comes from big iron routers where regular packets are forwarded in routing tables using ASICs and fancy cache memory, but controller cpu being a puny atom would melt if you sent it a few gigabits worth of ping traffic down an interface, and you could dos the router. I think I don’t need that at home.

1 Like