Raspberry Pi : Configuring a Time Capsule/Backintime server

In this post, I am setting up a Time Capsule and Backintime server. I am using a Raspberry Pi that has Ubuntu installed, with a USB disk that has been configured into a ZFS pool.

Setting up backup users

You are going to have to create users for each of the services/users that will be connecting to the server. You want to keep files and access as isolated as possible. As in a given user shouldn’t have any visibility or notion of other users’ backups. We are also creating accounts that can’t login into the system for Time Machine, only authenticate.

Check if there is an entry for nologin in:

$ cat /etc/shells

If there is no entry add it:

# vim /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
/usr/bin/tmux
/usr/sbin/nologin

Create a generic user for the backups, or dedicated accounts for each user to increase security:

Generic user example:

# useradd -s /usr/sbin/nologin timemachine
# passwd timemachine

Dedicated user example:

# useradd -s /usr/sbin/nologin timemachine_john
# passwd timemachine_john

Note that useradd doesn’t create a home

If required, the default shell can be changed with:

# usermod -s /usr/sbin/nologin timemachine_john

Setting up backup user groups

If more than one system is going to be backed up it is advisable to use different accounts for each.

It is possible to isolate users by assigning them individual datasets, but that might create storage silos.

An alternative is to create individual users that belong to the same backup group. The backup group can access the backintime dataset, but not each other’s data.

Create the group.

# addgroup backupusers

Assign main group and secondary group (the secondary group would be the shared one).

# usermod -g timemachine_john -G backupusers timemachine_john

Although not required, you could force the UID and GID to be a specific one.

# usermod -u 1012 timemachine
# groupmod -g 1012 timemachine

Time Capsule

Install netatalk

Install netatalk from the repositories.

# apt install netatalk

Allow access to all the appropriate accounts to the directory where the backups are going to be written to:

# chown :timemachine_john /backups/timecapsule/
# chmod 775 /backups/timecapsule/

Edit the settings of the netatalk service so that that share can be seen with the name of your choice and work as a Time Capsule server.

# vim /etc/netatalk/AppleVolumes.default

Enter the following:

/backups/timecapsule "pi-capsule" options:tm

Note that you can give the capsule a name with spaces above.

Restart the service:

# systemctl restart netatalk

Check that netatalk has been installed correctly:

# afpd -V

afpd 3.1.12 - Apple Filing Protocol (AFP) daemon of Netatalk
[...]
afpd has been compiled with support for these features:

          AFP versions: 2.2 3.0 3.1 3.2 3.3 3.4 
         CNID backends: dbd last tdb 
      Zeroconf support: Avahi
  TCP wrappers support: Yes
         Quota support: Yes
   Admin group support: Yes
    Valid shell checks: Yes
      cracklib support: No
            EA support: ad | sys
           ACL support: Yes
          LDAP support: Yes
         D-Bus support: Yes
     Spotlight support: Yes
         DTrace probes: Yes

              afp.conf: /etc/netatalk/afp.conf
           extmap.conf: /etc/netatalk/extmap.conf
       state directory: /var/lib/netatalk/
    afp_signature.conf: /var/lib/netatalk/afp_signature.conf
      afp_voluuid.conf: /var/lib/netatalk/afp_voluuid.conf
       UAM search path: /usr/lib/netatalk//
  Server messages path: /var/lib/netatalk/msg/

Configure netatalk

# vim /etc/nsswitch.conf

Change this line:

hosts:          files mdns4_minimal [NOTFOUND=return] dns

to this:

hosts:          files mdns4_minimal [NOTFOUND=return] dns mdns4 mdns

Note that if you are running Netatalk 3.1.11 or above it is not necessary any more to create the /etc/avahi/services/afpd.service. Using this file will cause an error.

If you are running an older version go ahead, otherwise jump to the next section.

Create /etc/avahi/services/afpd.service as root

# vim /etc/avahi/services/afpd.service

and fill it up with:

<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
        <name replace-wildcards="yes">%h</name>
        <service>
                <type>_afpovertcp._tcp</type>
                <port>548</port>
        </service>
        <service>
                <type>_device-info._tcp</type>
                <port>0</port>
                <txt-record>model=TimeCapsule</txt-record>
        </service>
</service-group>

Configure the AFP service

Edit the configuration file.

# vim /etc/netatalk/afp.conf
[Global]
; Global server settings
mimic model = TimeCapsule6,106

[pi-capsule]
path = /backups/timecapsule
time machine = yes

Check configuration and reload if needed:

# systemctl status avahi-daemon	

[restart if necessary]
# systemctl restart netatalk

[Make the service automatically start]
# systemctl enable netatalk.service

If you go to your Mac’s Time Machine preferences the new volume will be available and you can start using it.

netatalk troubleshooting

Some notes of things to check from the server side (Time Capsule server):

https://wiki.archlinux.org/index.php/Netatalk

Backintime setup

Configuring Backintime

Prepare users

If you have disabled passwords and are only using keys, you will need to temporarily change the security settings to allow Backintime to exchange keys.

On the remote system/Pi/server:

# vim /etc/ssh/sshd_config
PasswordAuthentication yes
# systemctl restart ssh

Backintime uses SSH, so the user accounts need to be allowed to login. Therefore the default login shell needs to reflect this.

If not created already, assign the user a home directory. Finally, allow the user to read and write the folder containing the backups.

# usermod -s /usr/bin/bash backintime_john

# mkdir /home/backintime_john

# chown backintime_tuxedo:backintime_john /home/backintime_john/

# usermod -d /home/backintime_john/ backintime_john

# chown :backupusers /backups/backintime/

# chmod 775 /backups/backintime/

Permissions for some of the subfolders might be required in multi-user configuration after the first backup:

# chown :backupusers /backups/backintime*/backintime

# chmod 770 /backups/backintime*/backintime/system1/
# chmod 770 /backups/backintime*/backintime/laptop2/

Prepare keys

To simplify things these are the roles:

[Local system]
The client machine that is running Backintime and that you want to backup your data from.

[Remote system]
The SSH server that has the storage where your backup is going to be stored.

From the local system account you want to run backintime (either your user or root, depending on how you run Backintime) SSH into the remote system. In my case, a Raspberry Pi.

# ssh backintime_john@pi-capsule.local

After logging in check the host key.

$ ssh-keygen -l -f /etc/ssh/ssh_host_ecdsa_key.pub
256 SHA256:KjzU6aGqH6tXri/K87xz3H+cP35PMT7n+Ob6MIaBZb0 root@pi-capsule (ECDSA)

You can then log out from the remote machine.

From the local account, you want to run Backintime from generate a new SSH key pair.

# ssh-keygen

And then copy the public key to the Pi.

# ssh-copy-id -i ~/.ssh/id_rsa.pub backintime_david@pi-capsule
[...]
ECDSA key fingerprint is SHA256:KjzU6aGqH6tXri/K87xz3H+cP35PMT7n+Ob6MIaBZb0.
[...]
Number of key(s) added: 1
[...]

Note that the fingerprint is the same as the one displayed in the previous step.

Configure Backintime profile

You can now configure the SSH profile from Backintime and make the first run.

In the General tab:

Mode:               SSH

SSH Settings
Host:   pi-capsule
Port:   22
User:   backintime_david
Path:   /backups/backintime_david
Cipher:     [Leave as default]
Private Key:/root/.ssh/id_rsa

Password
SSH private key:[empty in most cases]
Enable Cache Password

Advanced
Host:       tuxedo
User:       root
Profile:    2

Schedule
[Select appropriate setting after testing]
Include

/home
/etc/
/boot
/root
/steam
/opt
/usr
/var
Exclude (example)

/steam/steamapps/downloading
/var/cache
/vm/kvm_images/__security/Security_TryHackMe*.qcow2
Auto-remove
Older than 10 years
If free space is less than 50GiB
If free inodes is less than 2%

Smart remove
Run in background on remote Host
Keep last
14 days (7 days)
21 days (14 days)
8 weeks (6 weeks)
36 months (14 months)

Don't remove named snapshots
Options
Enable notifications
Backup replaced files on restore
Continue on errors (keep incomplete snapshots)
Log level: Changes & Errors

After the first run has completed you can check which is the best performing cipher from the CLI.

# backintime benchmark-cipher --profile-id 2

After a few rounds, aes192-ctr came out as the best performing cipher for me.

Secure SSH

If you changed the SSH configuration at the beginning, after setting everything up, remember to secure SSH again on the server/remote system.

# vim /etc/ssh/sshd_config
PasswordAuthentication no
# systemctl restart ssh

Restoring restrictions to backup users

The login account is required for Backintime to be able to run rsync. It is worth doing a bit more research on how to harden/limit these accounts.

Troubleshooting

Some examples of some issues and some troubleshooting steps you can apply.

Time Capsule can’t be reached / firewall settings

Make sure the server is allowing AFP connections from the Mac client.

# ufw allow proto tcp from CLIENT_IP to PI_CAPSULE_IP port 548

Time Capsule – Configuring Time Machine backups via the network on a macOS VM

The destination needs to be configured manually.

Mount the AFP/Time Capsule mount via the Finder.

In the CLI configure the destination:

# tmutil setdestination -a /Volumes/pi-capsule

The backups can then be started from the GUI.

You can get information about the current configured destinations via the CLI.

# tmutil destinationinfo
====================================
Name            : pi-capsule
Kind            : Network
Mount Point     : /Volumes/pi-capsule
ID              : 7B648734-9BFC-417F-B5A1-F31B8AD52F4B

Time Capsule – Checking backup status

# tmutil currentphase
# tmutil status

ZFS stalling on a Raspberry Pi

Check the recordsize property. Reduce it to the default 128 kiB.

Reduce ARC size to reduce the amount of memory consumed/reserved for ZFS.

Understanding rsync logs

The logs indicate the type of change rsync is seeing. A reference is available here:

XYcstpoguax  path/to/file
|||||||||||
||||||||||╰- x: The extended attribute information changed
|||||||||╰-- a: The ACL information changed
||||||||╰--- u: The u slot is reserved for future use
|||||||╰---- g: Group is different
||||||╰----- o: Owner is different
|||||╰------ p: Permission are different
||||╰------- t: Modification time is different
|||╰-------- s: Size is different
||╰--------- c: Different checksum (for regular files), or
||              changed value (for symlinks, devices, and special files)
|╰---------- the file type:
|            f: for a file,
|            d: for a directory,
|            L: for a symlink,
|            D: for a device,
|            S: for a special file (e.g. named sockets and fifos)
╰----------- the type of update being done::
             <: file is being transferred to the remote host (sent)
             >: file is being transferred to the local host (received)
             c: local change/creation for the item, such as:
                - the creation of a directory
                - the changing of a symlink,
                - etc.
             h: the item is a hard link to another item (requires 
                --hard-links).
             .: the item is not being updated (though it might have
                attributes that are being modified)
             *: means that the rest of the itemized-output area contains
                a message (e.g. "deleting")

Some example output:

>f+++++++++ some/dir/new-file.txt
.f....og..x some/dir/existing-file-with-changed-owner-and-group.txt
.f........x some/dir/existing-file-with-changed-unnamed-attribute.txt
>f...p....x some/dir/existing-file-with-changed-permissions.txt
>f..t..g..x some/dir/existing-file-with-changed-time-and-group.txt
>f.s......x some/dir/existing-file-with-changed-size.txt
>f.st.....x some/dir/existing-file-with-changed-size-and-time-stamp.txt 
cd+++++++++ some/dir/new-directory/
.d....og... some/dir/existing-directory-with-changed-owner-and-group/
.d..t...... some/dir/existing-directory-with-different-time-stamp/ 



Raspberry Pi: Installing, hardening and optimising Ubuntu 20.04 Server

I have been trying to document the process of configuring a Raspberry Pi as a Time Machine Capsule, but the article became far too long. It covered far too much information and was really hard to read.

I then decided to break the stages into more manageable steps. This has the advantage of allowing the common stages, like setting up the OS, to be shared between different projects.

Therefore, this is that first entry. Some others will follow about how to build different things from this first base image.

Selecting the OS

The 64-bit beta release of Raspberry Pi OS I tried didn’t let ZFS install easily. Ubuntu has the advantage of being a like for like experience regardless of the platform, so it is my preferred choice. Any experience you gain with it will be easily transferable.

You can download Ubuntu Server images from https://ubuntu.com/download/raspberry-pi. The LTS version is also the preferred one.

The Raspberry Pi model will determine the supported versions of the OS.

Model 32-bit Ubuntu 64-bit Ubuntu
Raspberry Pi 2 Supported Not supported
Raspberry Pi 3 Supported Recommended
Raspberry Pi 4 Supported Recommended
Supported Ubuntu versions.

The Raspberry Pi 3 has limited benefits when using the 64-bit image due to its limited RAM. In addition, it won’t support ZFS for the same reason. The Pi will restart/reset when ZFS volumes are accessed due to a lack of RAM.

If you are going to use a GUI, you should choose a Raspberry Pi 4 with at least 4GB of RAM.

The image can be directly installed on a micro SD card:

# ddrescue -y -c 4Ki ubuntu-20.04.3-preinstalled-server-arm64+raspi.img /dev/sdxx

Installing Ubuntu Server on a USB stick

It is possible to boot from a USB stick, which is preferable for several reasons. They are cheaper, easier to access from another system, and simple to replace.

First, enable USB boot on your Pi.

Model USB Boot Support Notes
Raspberry Pi 1 Not supported n/a
Raspberry Pi 2 and 3B Supported On Raspberry Pi OS echo program_usb_boot_mode=1 | sudo tee -a /boot/config.txt and reboot.
Raspberry Pi 3B+ Supported Supported out of the box
Raspberry Pi 4 Supported On Raspberry Pi OS rpi-eeprom-config --edit and set BOOT_ORDER=0xf41 and reboot.
Raspberry Pi’s with supported USB boot.

You might have to boot from an SD card at least once to configure USB boot. Once enabled, it remains activated.

Additional information about the different boot modes for the Raspberry Pi

The following links are provided for reference.

Raspberry Pi booting from USB mass storage https://www.raspberrypi.org/documentation/computers/raspberry-pi.html#booting-from-usb-mass-storage

Raspberry Pi 4 bootloader configuration https://www.raspberrypi.org/documentation/computers/raspberry-pi.html#raspberry-pi-4-bootloader-configuration

Raspberry Pi 4 boot flow https://www.raspberrypi.org/documentation/computers/raspberry-pi.html#raspberry-pi-4-boot-flow

Configuration steps

Once the Pi has been configured to boot from a USB device, install the image on a USB stick like the SD card.

# ddrescue -y -c 4Ki ubuntu-20.04.3-preinstalled-server-arm64+raspi.img /dev/sdxx

For the image to be bootable, you need to make some changes. I extracted the steps from this Raspberry Pi forum post. You might find it easier to apply changes if you mount it on another system.

There are two options to make the changes:

  • Mount the USB stick on another system, and then issue the commands on the USB device. This other system can be the Raspberry Pi itself booting from the SD card, and accessing the USB device.
  • Or make the changes on the SD card, and then copy the SD card image to the USB device.

Apply the following changes.

1) On the /boot of the USB device, uncompress vmlinuz.

$ cd /media/*/system-boot/
$ zcat vmlinuz > vmlinux

2) Update the config.txt file. The pi4 section is shown in this example, but it has also been tested on a Pi 3. Just enter the information for your Pi model.

$ vim config.txt

The dtoverlay line might be optional for headless systems, but if you have the time and inclination, there is some documentation regarding Raspberry Pi’s device tree parameters.

[pi4]
kernel=vmlinux
max_framebuffers=2
dtoverlay=vc4-fkms-v3d
boot_delay
initramfs initrd.img followkernel

3) Create a script in the boot partition called auto_decompress_kernel with the following content:

#!/bin/bash -e

## Set Variables

BTPATH=/boot/firmware
CKPATH=$BTPATH/vmlinuz
DKPATH=$BTPATH/vmlinux

## Check if compression needs to be done.

if [ -e $BTPATH/check.md5 ]; then
	if md5sum --status --ignore-missing -c $BTPATH/check.md5; then
    	echo -e "\e[32mFiles have not changed, Decompression not needed\e[0m"
	    exit 0
	else
        echo -e "\e[31mHash failed, kernel will be compressed\e[0m"
	fi
fi

# Backup the old decompressed kernel

mv $DKPATH $DKPATH.bak

if [ ! $? == 0 ]; then
	echo -e "\e[31mDECOMPRESSED KERNEL BACKUP FAILED!\e[0m"
	exit 1
else
    echo -e "\e[32mDecompressed kernel backup was successful\e[0m"
fi

#Decompress the new kernel
echo "Decompressing kernel: "$CKPATH".............."

zcat $CKPATH > $DKPATH

if [ ! $? == 0 ]; then
	echo -e "\e[31mKERNEL FAILED TO DECOMPRESS!\e[0m"
	exit 1
else
	echo -e "\e[32mKernel Decompressed Succesfully\e[0m"
fi

# Hash the new kernel for checking
md5sum $CKPATH $DKPATH > $BTPATH/check.md5

if [ ! $? == 0 ]; then
    	echo -e "\e[31mMD5 GENERATION FAILED!\e[0m"
	else
        echo -e "\e[32mMD5 generated Succesfully\e[0m"
fi

# Exit
exit 0

Normally you would need to mark the script as executable, but unless you modify the partition from its FAT32 default, there is no executable flag to set. So leave it as it is.

If you can mount the root filesystem in the system you are using to edit the files, you can go ahead with steps 4 and 5. Otherwise, you should be able to boot now and manually do these steps after your first boot.

4) Create a script in /ect/apt/apt.conf.d/ directory and call it 999_decompress_rpi_kernel

# cd /media/*/writable/etc/apt/apt.conf.d/
# vi 999_decompress_rpi_kernel

Fill the file with the following content:

DPkg::Post-Invoke {"/bin/bash /boot/firmware/auto_decompress_kernel"; };

5) Make the script executable.

# chmod 744 999_decompress_rpi_kernel

You can save yourself some time and configure the network at this stage.

In my case, I have a static DHCP lease associated with the Pi MAC address, but if you don’t, you can configure the network with a static IP address by editing the network-config file in /boot.

$ cd /media/*/boot/
$ vim network-config

An example of a static address entry would be:

version: 2
ethernets:
  eth0:
    dhcp4: no
    addresses: [192.168.1.201/24]
    gateway4: 192.168.1.254
    nameservers:
       addresses: [192.168.1.254]

You can eject the USB drive, insert it on your Raspberry Pi and boot.

Setting up Ubuntu

The default user name and password are ubuntu / ubuntu.

Upon login, you will be asked to change your password. We will delete this user in the following steps to increase security.

Run an update:

$ sudo su
# apt update
# apt upgrade

Setting up users

Create a new user (or change the name of the existing user).

# adduser <newuser>

Extract the groups for the user ubuntu and compare them with the new user.

# id ubuntu ; echo ; id <newuser>

uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),115(netdev),118(lxd)

uid=1001(newuser) gid=1001(newuser) groups=1001(newuser)

Add the new user to the same groups.

# usermod -a -G adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev,lxd newuser

Hostname

Set the hostname of your choice.

# hostnamectl set-hostname <system_name>

[Check the change]

# hostnamectl
   Static hostname: pi-capsule
         Icon name: computer
        Machine ID: db0a1818241a47e178f229294f6864ae
           Boot ID: 983818fbaa8246348066c36f2237636e
  Operating System: Ubuntu 20.04.2 LTS
            Kernel: Linux 5.4.0-1029-raspi
      Architecture: arm64

Date and time

Set the time zone.

# timedatectl set-timezone Europe/London

Configure the time sources by editing /etc/systemd/timesyncd.conf.

[Time]
NTP=uk.pool.ntp.org
FallbackNTP=ntp.ubuntu.com

Restart the service.

# systemctl restart systemd-timesyncd.service

Check the status and check that the time source is correct.

# systemctl status systemd-timesyncd.service

Finally, check that the time zone is correct.

# timedatectl status
               Local time: Sun 2021-08-29 23:24:49 BST
           Universal time: Sun 2021-08-29 22:24:49 UTC
                 RTC time: n/a                        
                Time zone: Europe/London (BST, +0100) 
System clock synchronized: yes                        
              NTP service: active                     
          RTC in local TZ: no

Customising the MOTD

You can get the MOTD from the login screen manually with the following command.

$ for i in /etc/update-motd.d/* ; do if [ "$i" != "/etc/update-motd.d/98-fsck-at-reboot" ]; then $i; fi; done

To get system information (including temperature):

$ /etc/update-motd.d/50-landscape-sysinfo

You can edit, add and reorder scripts in /etc/update-motd.d/.

Configuring SSH

SSH will be enabled by default. Test access with the newly created account.

By default, only the password is required to access the server, but we will add the requirement of needing an SSH key with the password. And also limit access only from authorised IP addresses.

If you haven’t generated a public and private key pair on your system (the one used to log into the Pi), you will need to do it (explained below).

A brief note on encryption. Elliptic curve cryptography (ECC) generates smaller keys and provides faster encryption than non-ECC. The smaller ECC keys also provide an equivalent level of encryption provided only with bigger RSA keys:

ECC key size RSA equivalent
160 bits 1024 bits
224 bits 2048 bits
256 bits 3072 bits
384 bits 7680 bits
512 bits 15360 bits
ECC uses smaller keys with higher equivalent security.

You can use either ECDSA or ED25519 keys. ED25519 isn’t as universally implemented yet due to being quite new, so some clients might not support it, but it is the fastest and most secure one.

For both types of encryption, it is recommended to use the bigger key size. This is 521 bits for ECDSA (note that 521 isn’t a typo). ED25519 keys have a fixed length of 512 bits.

When issuing ssh-keygen, use the -o option. This forces the use of the new OpenSSH format (instead of PEM) when saving your private key. It increases resistance to a known brute-force attack. It breaks compatibility with OpenSSH versions older than 6.5, but this version of Ubuntu runs version 8.2, so this isn’t an issue.

More information about SSH key generation is available here: https://www.ssh.com/ssh/keygen/

The steps are:

Create a suitable key pair with:

$ ssh-keygen -o -t ed25519

[or]

$ ssh-keygen -o -t ecdsa -b 521

Copy the public key to the Ubuntu server. It can be done manually, but it is best to use the appropriate tool:

$ ssh-copy-id -i ~/.ssh/<myprivatekey> <user>@<remotehost>

Note that you use the -i flag with your private key, and ssh-copy-id will send the public key for storage on the remote host.

SSH can be configured on the server side to allow only password logins, only key logins, or to require both.

# vim /etc/ssh/sshd_config

PasswordAuthentication no” will only use the key, and “PasswordAuthentication yes” will use both password and key. Obviously, the second option is safer.

We also disable the option to allow root to login via SSH. The root account is disabled on the image by default, but ensure SSH has been configured correctly anyway.

PermitRootLogin no
PasswordAuthentication yes
# systemctl restart sshd

SSH from another terminal with the new user account, and ensure that the access is working.

If it works, delete the old ubuntu account.

# userdel -r ubuntu

Activate and configure the firewall

Set default rules (deny all incoming, allow all outgoing).

# ufw status

# ufw default allow outgoing

# ufw default deny incoming

UFW requires IPv6 to be enabled. It can be made to work with it disabled, but how to achieve that is out of the scope of this post.

# vim /etc/default/ufw

IPV6=yes

Allow SSH.

# ufw allow ssh

[but preferably allow only specific clients:]

# ufw allow proto tcp from <SOURCE> to <SERVER> port 22

And limit the allowed connection attempts to thwart brute force attacks:

ufw limit ssh

Enable the firewall and check the rules:

# ufw enable

# ufw status

[List rules with numbers]

# ufw status numbered

Remember that if you are using IPv6, you might need to edit rules accordingly.

Install log2ram

To reduce the number of writes on the USB drive/SD card, you can use the RAM disk utility log2ram.

https://github.com/azlux/log2ram

Not only that, it will speed up the performance of the Raspberry Pi in exchange for a small amount of RAM.

Install:

# echo "deb http://packages.azlux.fr/debian/ buster main" | sudo tee /etc/apt/sources.list.d/azlux.list

# wget -qO - https://azlux.fr/repo.gpg.key | sudo apt-key add -

# apt update

# apt install log2ram

Configure the service. The SIZE entry depends on your system; 256M is a lot for a Pi with only 1GB of RAM.

# vim /etc/log2ram.conf

SIZE=256M
USE_RSYNC=true
MAIL=true
PATH_DISK="/var/log"

And restart.

# reboot

Check that the service is working:

$ systemctl status log2ram
$ df -h | grep log2ram
log2ram         256M  106M  151M  42% /var/log

Installing additional utilities

Install your choice of applications.

# apt install mosh tmux pydf vim-nox glances iotop

Mosh might require some ports to be opened in the firewall.

The range of ports goes from 60001 to 60999, but if you are expecting few connections, you can make the range smaller.

# ufw allow proto udp from <SOURCE> to <SERVER> port 60001:60010

# ufw limit 60001:60010/udp

Install Cockpit

# apt install -y cockpit
# ufw allow proto tcp from <SOURCE< to <SERVER> port 9090

# ufw limit 9090/tcp

The system can now be reached via the web browser via port 9090:

https://<hostname/IP>:9090

Other customisation

Argon Fan HAT configuration

If you have an Argon fan HAT, you can configure it as follows.

$ curl https://download.argon40.com/argonfanhat.sh -o argonfanhat.sh
$ bash argonfanhat.sh
[...]
Use argonone-config to configure fan
Use argonone-uninstall to uninstall

I have configured with the following triggers.

  • 30 ºC -> 0%
  • 60 ºC -> 10%
  • 65 ºC -> 25%
  • 70 ºC -> 55%
  • 75 ºC -> 100%

Aliases

On Ubuntu, and most distros, there will be an entry in ~/.bashrc that will look like this:

if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi

This entry can be added manually if not present. This allows all of the aliases to be grouped in ~/.bash_aliases.

$ vim ~/.bash_aliases
# Show free RAM
alias showfreeram="free -m | sed -n '2 p' | awk '{print $4}'"

# Release and free up RAM
# alias freeram='freeram && sync && sudo echo 3 | sudo tee /proc/sys/vm/drop_caches && freeram -m'

# Show temperature
alias temp='cat /sys/class/thermal/thermal_zone0/temp | head -c -4 && echo " C"'

# Show ZFS datasets compress ratios
alias ratio='sudo zfs get all | grep " compressratio "'

This would create a base image with a decent level of security. I will likely add how to add Fail2Ban to improve security even further.




Linux: Adding a GUI to headless/server installs

Server and minimal installs are normally headless and have no graphical interface.

If needed you can add a GUI manually. The process is slightly different depending on the distro.

RedHat / CentOS 7.x

# yum update
# yum groupinstall "Server with GUI"

RedHat / CentOS 8.x

# dnf update
# dnf groupinstall workstation

Ubuntu 18.04.x LTS

# apt update

[Install minimum GNOME desktop]
# apt install --no-install-recommends ubuntu-desktop

[Install full desktop with associated applications]
(Long process and too many extras installed)

# apt install ubuntu-desktop

[There are other alternative desktops and installations possible:]

[Generic Gnome desktop]
# apt install vanilla-gnome-desktop

[Mate]
# apt install ubuntu-mate-desktop

[Xfce]
# apt install xubuntu-desktop

[KDE]
# apt install kubuntu-desktop

[LightDM]
# apt install --no-install-recommends lightdm

Debian 9.x

# apt update
# apt install gnome-core

Debian 10.x

# apt update
# apt install gnome-core

All the above distros use systemd as their init system and you set the default run level with the same set of commands.

[Enable run level 5 by default]
systemctl  set-default graphical.target

[Enable run level 3 by default]
systemctl  set-default multi-user.target

Despite systemd you can still use init to start the graphical interface without having to reboot.

# init 5



Ubuntu: Fixing network interfaces not showing in GUI on Ubuntu Server

Ubuntu Server and Ubuntu Desktop use different network managers. Ubuntu Server uses networkd which doesn’t have a GUI component. So after adding a desktop to Ubuntu Server you won’t be able to see or change the configuration via the GUI by default.

Ubuntu Desktop Ubuntu Server
Configuration tool netplan netplan
CLI manager nmcli networkd
GUI manager NetworkManager n/a

For you to be able to see the network settings on the GUI you need to switch from networkd to NetworkManager.

Install NetworkManager:

apt install network-manager network-manager-gnome network-manager-openvpn network-manager-openconnect network-manager-openvpn-gnome

Check which systemd network daemons are active:

systemctl list-unit-files | grep -i network

Disable/stop networkd services:

systemctl disable systemd-networkd.service
systemctl disable networkd-dispatcher.service
systemctl stop systemd-networkd
systemctl stop systemd-networkd.socket
systemctl stop systemd-networkd.service
systemctl stop network-dispatcher.service

Remember that nmcli can be used for CLI NetworkManager control also.

Network interfaces are listed in yaml files in /etc/netplan/

Edit /etc/netplan/50-cloud-init.yaml and edit the line or add a line after the version: 2 entry to be renderer: Networkmanager.

network:
  ethernets:
    eno1:
      dhcp4: true
  version: 2
  renderer: NetworkManager

Check the changes and apply them:

netplan generate
netplan try / netplan apply