Home

Base System

Install the Aarch64 base system from Arch for ARM project. Install it to the SD card, according to the manual. Boot up, get the IP (e.g. router interface or local login), connect via ssh: - login alarm:alarm - login root:root

Base setup

Storage drive

I attach a large USB hard drive to the pi as storage unit. At various points some folders will be mapped to other parts of the file system, so that the services save their data on it. The drive is assumed to be mounted at /srv/storage.

Create an fstab entry:

# let the drive be automounted by systemd upon first access
UUID=38593866-f71b-4359-b61e-f14909ab80a8 /srv/storage ext4 rw,async,auto,nofail,nodiratime,x-systemd.device-timeout=5 0 1

Important options to note are auto,nofail. In this combination, bootup will not be obstructed if the device is not available. On the other side it will automatically be mounted as soon as it is connected to the system. The option x-systemd.device-timeout is the time in seconds the system will block I/O operations on paths to the device, if it is not available before giving an error. 5 seconds is an acceptable parameter here.

Transferring files via network

When transferring files with putty or ssh/scp there are two things to consider:

  • Use the scp protocol, it puts less strain on the pi (putty might default to the sftp protocol)

  • Use multiple copy instances (2 or three), as a single one might cap out on the CPU before it saturates the network link. Alternatives might be rsync or netcat (not encrypted) but I haven’t tried them yet (scp was fine enough).

Switching to the original pi kernel

First is of course building the kernel. Use a cross-toolchain, as building on the pi is really slow. Some additional Variables have to be passed to make, produce the right result:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- KCFLAGS=-mcpu=cortex-a53 <target>

Target is in order the following three:

The built image can then be copied from arch/arm64/boot.

On the pi an inintramfs has to be created with mkinitcpio -k 4.9.65-v8+ -r /usr -g /boot/initramfs-linux-pi.img, assuming 4.9.65-v8+ is the kernel version (and folder where modules are stored).

Then the boot.txt has to edited to use the new image and initramfs. Afterwards it has to be processed with mkscr.

My self-built kernel does not want to start when built this way, so kept the arch one. But in theory this is the way to go.

Base software

Tools that come in handy when working on the pi: - vim - uboot-tools (only when updating the boot loader) - mlocate - rsync

User config

Change the root password! Obviously. Create a user to use as primary working user. Make him part of the groups wheel,users,http. Group wheel to gain root privileges as needed, http to write to the web directory when uploading files with scp or rsync.

Group for user with access to shared files

I create a group that contains all users/services which need access to shared files. This affects the main ssh user (loebl) as well as all file sharing services (transmission, http, samba). Using a common group is a rather convenient way to serve a file through different tools.

groupadd netusers
usermod -a -G netusers loebl

SSHd config

The configuration for sshd is in /etc/ssh/sshd_config. Generate a Keypair with ssh-keygen. The private key should be password protected. Transport the public key to the user folder of the login account and append it to ~/.ssh/authorized_keys. A valid entry looks like this (for an ed25519 key):

ssh-ed25519 <base64-stuff> <key-comment>

Change the sshd_config to disallow root login under any circumstances and all other user only by certificate:

Firewall config

To configure the firewall I will use nftables, which replaces iptables. To investigate current port usage I run ss -a -p -4 (show all ports with process name for IPv4). Other useful flags are -l (show only listening ports) and -6 (Show IPv6 ports). Ressources for this part are nftables Wiki and Arch Wiki for nftables

Use pacman -S nftables to get the nftables userspace tools. On Arch this package also comes with a little example file which drops all incoming packets except ICMP and ssh.

Basics

To start filtering packets, you first need to define a table for a specific protocoll. Relevant here are IPv4 and IPv6. They can both be addressed with the inet table family. Each table contains one or more chains, which contain one or more rules.

Packets which aren’t matched by any rule pass through unhindered. There aren’t any rules, chains or tables unless they are specified in the configuration (iptables had some pre-defined chains).

Rules can be created with the command line utility nft. They can also be written into a config file to be loaded at boot time with nft -f. Currently loaded rules can be viewed with nft list ruleset.

Simple setup

In arch the nftalbes service will read in the config file /etc/nftables.conf. Per default it contains rules to drop every incoming packet except ssh connections (which is an already usable base setup). I will recreate this here, but change the structure. First I will create the basic table used for all my purposes:

#!/usr/bin/nft -f

table inet myrules {
}

In this table I set a chain on the input hook. This way incoming packets for local processes go through the chain:

chain incoming {
  type filter hook input priority 0; policy drop;
}

Other hooks are prerouting (run before deciding if the packet gets forwarded or to a local process), forward (run on forwarded packets), output (packets emitted from local processes) and postrouting (all outgoing packets, forwarded and from processes). Priority can be set if there are multiple chains for the same hook. The smaller the number, the higher the priority. Negative numbers are supported.

The policy statement is the default action if no rule matches. The statement above basically implements a whitelist, every packet not allowed is dropped.

Now some basic rules are added in the chain:

# allow packets for existing connections
ct state {established, related} accept
ct state invalid reject

# allow packets from loopback device unhindered
iifname lo accept

# allow ICMP packets
ip protocol icmp accept
ip6 nexthdr icmpv6 accept

The first line will accept any packets belonging to an already established connection (usually TCP connections). The next one will drop packets which couldn’t be matched to a connection and send an ICMP port unreachable packet. For public servers some like to change this to drop. Next we accept any packets coming from the loopback device. Also ICMP packets are all accepted.

After these I include my per-application rules from a subdirectory, with one file per application:

include "/etc/nft_rules/in_*"
counter reject

The leading “/etc” is important to have nft look in the correct directory for included files and directories. It searches only in a directory specified at compile time (/usr/share for my distribution) or additionally in paths given with -I on the commandline. The shell wildcard is supported to include many files in one statement. Not finding an included file is not treated as an error, so care should be taken to ensure files are found (or you will end up closing the box). The last line will emit an ICMP packet for every request not served until now. The counter command will install a counter, so the number of rejected packets will be counted.

The only application rule for now are sshd and LLMNR. Create the file nft_rules/in_sshd.conf with the following content:

tcp dport 22 accept

The folder nft_rules should be a subdirectory of /etc. SSH only requires a single rule: accept TCP packets on port 22. In this case one could also write ssh instead of 22, as nftables has a list of well known ports.

Then in the same manner create the rule file in_llmnr.conf with the content:

udp dport 5355 accept
tcp dport 5355 accept