2020-12-07 10:03:03 -05:00
<!--
2021-02-23 14:02:20 -05:00
Title: How to configure hardened Raspberry Pi
Description: Learn how to configure hardened Raspberry Pi.
2020-12-07 10:03:03 -05:00
Author: Sun Knudsen < https: / / github . com / sunknudsen >
Contributors: Sun Knudsen < https: / / github . com / sunknudsen >
Reviewers:
Publication date: 2020-11-27T10:00:26.807Z
Listed: true
-->
2021-02-23 14:02:20 -05:00
# How to configure hardened Raspberry Pi
2020-12-07 10:03:03 -05:00
2021-02-23 14:02:20 -05:00
[](https://www.youtube.com/watch?v=6R8uKdstnts "How to configure hardened Raspberry Pi")
2020-12-11 08:39:56 -05:00
2020-12-07 10:03:03 -05:00
## Requirements
- [Raspberry Pi ](https://www.raspberrypi.org/ )
2021-09-27 07:52:49 -04:00
- Power adapter, keyboard and HDMI cable (and SD card reader if computer does not have one built-in)
2021-03-02 12:58:46 -05:00
- macOS computer
2020-12-07 10:03:03 -05:00
## Caveats
- When copy/pasting commands that start with `$` , strip out `$` as this character is not part of the command
- When copy/pasting commands that start with `cat << "EOF"` , select all lines at once (from `cat << "EOF"` to `EOF` inclusively) as they are part of the same (single) command
## Guide
2021-03-02 12:58:46 -05:00
### Step 1: create SSH key pair (on macOS)
2020-12-07 10:03:03 -05:00
When asked for file in which to save key, enter `pi` .
When asked for passphrase, use output from `openssl rand -base64 24` (and store passphrase in password manager).
2020-12-25 10:08:11 -05:00
```console
2021-05-10 08:25:40 +02:00
$ mkdir ~/.ssh
2020-12-07 10:03:03 -05:00
$ cd ~/.ssh
$ ssh-keygen -t rsa -C "pi"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sunknudsen/.ssh/id_rsa): pi
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in pi.
Your public key has been saved in pi.pub.
The key fingerprint is:
SHA256:NZkr0Bmu6tpQfDp2GHTIPyBz253uZk/gOmoqzEszGM0 pi
The key's randomart image is:
+---[RSA 3072]----+
| . |
| . . o o o |
| o * o + = |
| o * * + o o |
|. E = * S . |
|.. . * + o |
|++. * . o . |
|ooo=.o.oo. |
| o+++..+... |
+----[SHA256]-----+
$ cat pi.pub
2020-12-10 07:25:27 -05:00
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCzQpX9uqDP8L2gSZNJxYEi04Y1pZWz28v4zANY5dU6M35OFzXZcRcBqi2ZxiQofgxRrX9QlAcmcPFz8/CkpPw2WgQTflm+46ZrVEZcwwGwJsJwm7QVLQLd44/xtejEvMjzsuYDjJ1q4WhEvMSleTfOrix4yP0mjn83Zk1l6AMxR5J8DDumiHsGSYfcp+1XS9x4r4HP0mS2RpIy3rcoxLoJaYEKvVTj9qdvPMK7SDymZcvuBsgObEARVr77q4qhUfTP+xR91hHNEYD9FnCHF3qQBzlTlmTwpwhH6vOdWE3uUXCug9Ugw42Zj3PW0zd5rQ7EEpD9SDLbUqajpn2M5AlhkS9OrLpnIptocetRKNI9HzyAV1KqdNiQeL7/59d4y+HuZ9y032SaNzR1fw0nYMoHzTN9d+zPvziDZ183/pwtEXZNVVGzYO1r56n3S4vLx8YCpYqiHYVQVDF8aweoHYs3dAGAfPxmQ85+45UKpFR18XSGCqCO2fwbyTGDhkxCzU= pi
2020-12-07 10:03:03 -05:00
```
### Step 2: generate heredoc (the output of following command will be used at [step 10](#step-10-configure-pi-ssh-authorized-keys))
```shell
cat < < EOF
2021-02-23 14:02:20 -05:00
cat < < "_EOF" > ~/.ssh/authorized_keys
2020-12-07 10:03:03 -05:00
$(cat ~/.ssh/pi.pub)
_EOF
EOF
```
### Step 3: download latest version of [Raspberry Pi OS Lite](https://www.raspberrypi.org/software/operating-systems/)
2021-05-07 08:30:06 -04:00
### Step 4: copy “Raspberry Pi OS Lite” to SD card
2020-12-07 10:03:03 -05:00
> WARNING: DO NOT RUN THE FOLLOWING COMMANDS AS-IS.
2021-04-19 14:55:32 -04:00
Run `diskutil list` to find disk ID of SD card to overwrite with “Raspberry Pi OS Lite” (`disk2` in the following example).
2020-12-07 10:03:03 -05:00
2021-04-17 16:32:26 -04:00
Replace `diskn` and `rdiskn` with disk ID of SD card (`disk2` and `rdisk2` in the following example) and `2021-03-04-raspios-buster-armhf-lite.img` with current image.
2020-12-07 10:03:03 -05:00
```console
$ diskutil list
/dev/disk0 (internal, physical):
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *500.3 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_APFS Container disk1 500.1 GB disk0s2
/dev/disk1 (synthesized):
#: TYPE NAME SIZE IDENTIFIER
0: APFS Container Scheme - +500.1 GB disk1
Physical Store disk0s2
1: APFS Volume Macintosh HD - Data 340.9 GB disk1s1
2: APFS Volume Preboot 85.9 MB disk1s2
3: APFS Volume Recovery 529.0 MB disk1s3
4: APFS Volume VM 3.2 GB disk1s4
5: APFS Volume Macintosh HD 11.3 GB disk1s5
/dev/disk2 (internal, physical):
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *15.9 GB disk2
1: Windows_FAT_32 boot 268.4 MB disk2s1
2: Linux 15.7 GB disk2s2
$ sudo diskutil unmount /dev/diskn
disk2 was already unmounted or it has a partitioning scheme so use "diskutil unmountDisk" instead
$ sudo diskutil unmountDisk /dev/diskn (if previous step fails)
Unmount of all volumes on disk2 was successful
2021-05-05 13:00:52 -04:00
$ sudo dd bs=1m if=$HOME/Downloads/2021-03-04-raspios-buster-armhf-lite.img of=/dev/rdiskn
2020-12-07 10:03:03 -05:00
1772+0 records in
1772+0 records out
1858076672 bytes transferred in 40.449002 secs (45936280 bytes/sec)
$ sudo diskutil unmountDisk /dev/diskn
Unmount of all volumes on disk2 was successful
```
### Step 5: log in as pi (using keyboard) and change password using `passwd`
2021-02-23 14:02:20 -05:00
> Heads-up: current password is `raspberry`.
```console
$ passwd
Changing password for pi.
Current password:
New password:
Retype new password:
passwd: password updated successfully
```
2020-12-07 10:03:03 -05:00
### Step 6: configure Wi-Fi (if not using ethernet)
```shell
sudo raspi-config
```
Select “System Options”, then “Wireless LAN”, choose country, then select “OK”, enter “SSID”, enter passphrase.
### Step 7: enable SSH
```shell
sudo raspi-config
```
Select “Interface Options”, then “SSH”, then “Yes”, then “OK” and finally “Finish”.
When asked if you wish to reboot, select “No”.
### Step 8: find IP of Raspberry Pi (see `eth0` if using ethernet or `wlan0` if using Wi-Fi)
```shell
ip a
```
### Step 9: log in to Raspberry Pi over SSH
2020-12-10 08:16:05 -05:00
Replace `10.0.1.248` with IP of Raspberry Pi.
2020-12-07 10:03:03 -05:00
2021-03-02 12:58:46 -05:00
When asked for passphrase, enter passphrase from [step 5 ](#step-5-log-in-as-pi-using-keyboard-and-change-password-using-passwd ).
2020-12-07 10:03:03 -05:00
```shell
2020-12-10 08:16:05 -05:00
ssh pi@10 .0.1.248
2020-12-07 10:03:03 -05:00
```
### Step 10: configure pi SSH authorized keys
2021-05-21 13:21:41 -04:00
#### Create `.ssh` directory
2020-12-07 10:03:03 -05:00
```shell
2021-05-10 08:25:40 +02:00
mkdir ~/.ssh
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
#### Create `~/.ssh/authorized_keys` using heredoc generated at [step 2](#step-2-generate-heredoc-the-output-of-following-command-will-be-used-at-step-10)
2020-12-07 10:03:03 -05:00
```shell
2021-02-23 14:02:20 -05:00
cat < < "_EOF" > ~/.ssh/authorized_keys
2020-12-10 07:25:27 -05:00
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCzQpX9uqDP8L2gSZNJxYEi04Y1pZWz28v4zANY5dU6M35OFzXZcRcBqi2ZxiQofgxRrX9QlAcmcPFz8/CkpPw2WgQTflm+46ZrVEZcwwGwJsJwm7QVLQLd44/xtejEvMjzsuYDjJ1q4WhEvMSleTfOrix4yP0mjn83Zk1l6AMxR5J8DDumiHsGSYfcp+1XS9x4r4HP0mS2RpIy3rcoxLoJaYEKvVTj9qdvPMK7SDymZcvuBsgObEARVr77q4qhUfTP+xR91hHNEYD9FnCHF3qQBzlTlmTwpwhH6vOdWE3uUXCug9Ugw42Zj3PW0zd5rQ7EEpD9SDLbUqajpn2M5AlhkS9OrLpnIptocetRKNI9HzyAV1KqdNiQeL7/59d4y+HuZ9y032SaNzR1fw0nYMoHzTN9d+zPvziDZ183/pwtEXZNVVGzYO1r56n3S4vLx8YCpYqiHYVQVDF8aweoHYs3dAGAfPxmQ85+45UKpFR18XSGCqCO2fwbyTGDhkxCzU= pi
2020-12-07 10:03:03 -05:00
_EOF
```
### Step 11: log out
```shell
exit
```
2021-02-23 14:02:20 -05:00
### Step 12: log in
2020-12-07 10:03:03 -05:00
2020-12-10 08:16:05 -05:00
Replace `10.0.1.248` with IP of Raspberry Pi.
2020-12-07 10:03:03 -05:00
2021-04-04 07:26:38 -04:00
When asked for passphrase, enter passphrase from [step 1 ](#step-1-create-ssh-key-pair-on-macos ).
2020-12-07 10:03:03 -05:00
```shell
2020-12-10 08:16:05 -05:00
ssh pi@10 .0.1.248 -i ~/.ssh/pi
2020-12-07 10:03:03 -05:00
```
### Step 13: disable pi Bash history
```shell
sed -i -E 's/^HISTSIZE=/#HISTSIZE =/' ~/.bashrc
sed -i -E 's/^HISTFILESIZE=/#HISTFILESIZE =/' ~/.bashrc
echo "HISTFILESIZE=0" >> ~/.bashrc
2021-02-23 14:02:20 -05:00
history -c; history -w
2020-12-07 10:03:03 -05:00
source ~/.bashrc
```
2021-05-15 09:02:35 -04:00
### Step 14: configure pi `.vimrc`
2021-02-23 14:02:20 -05:00
```shell
cat < < "EOF" > ~/.vimrc
set encoding=UTF-8
set termencoding=UTF-8
set nocompatible
set backspace=indent,eol,start
set autoindent
set tabstop=2
set shiftwidth=2
set expandtab
set smarttab
set ruler
set paste
syntax on
EOF
```
2021-05-15 09:02:35 -04:00
### Step 15: switch to root
2020-12-07 10:03:03 -05:00
```shell
sudo su -
```
2021-05-15 09:02:35 -04:00
### Step 16: disable pi sudo `nopassword` “feature”
```shell
rm /etc/sudoers.d/010_*
```
2021-02-23 14:02:20 -05:00
### Step 17: disable root Bash history
2020-12-07 10:03:03 -05:00
```shell
echo "HISTFILESIZE=0" >> ~/.bashrc
2021-02-23 14:02:20 -05:00
history -c; history -w
2020-12-07 10:03:03 -05:00
source ~/.bashrc
```
2021-02-23 14:02:20 -05:00
### Step 18: set root password
2020-12-07 10:03:03 -05:00
When asked for password, use output from `openssl rand -base64 24` (and store password in password manager).
2021-02-23 14:02:20 -05:00
```console
$ passwd
New password:
Retype new password:
passwd: password updated successfully
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 19: configure root `.vimrc`
2020-12-07 10:03:03 -05:00
```shell
2021-02-23 14:02:20 -05:00
cat < < "EOF" > ~/.vimrc
set encoding=UTF-8
set termencoding=UTF-8
set nocompatible
set backspace=indent,eol,start
set autoindent
set tabstop=2
set shiftwidth=2
set expandtab
set smarttab
set ruler
set paste
syntax on
EOF
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 20: disable root login and password authentication
2020-12-07 10:03:03 -05:00
```shell
sed -i -E 's/^(#)?PermitRootLogin (prohibit-password|yes)/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i -E 's/^(#)?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
```
2021-02-23 14:02:20 -05:00
### Step 21: disable Bluetooth and Wi-Fi
2020-12-07 10:03:03 -05:00
> Heads-up: will take effect after reboot.
#### Disable Bluetooth
```shell
2021-02-23 14:02:20 -05:00
echo "dtoverlay=disable-bt" >> /boot/config.txt
2020-12-07 10:03:03 -05:00
```
#### Disable Wi-Fi (if using ethernet)
```shell
2021-02-23 14:02:20 -05:00
echo "dtoverlay=disable-wifi" >> /boot/config.txt
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 22: update APT index, install `iptables-persistent` and Vim and upgrade system
2020-12-07 10:03:03 -05:00
#### Update APT index
```shell
apt update
```
2021-02-23 14:02:20 -05:00
#### Install `iptables-persistent` and Vim
When asked to save current IPv4 or IPv6 rules, answer `Yes` .
```shell
apt install -y iptables-persistent vim
```
2020-12-07 10:03:03 -05:00
#### Upgrade packages
2021-04-19 09:54:52 -04:00
> Heads-up: if asked which version of `/etc/ssh/sshd_config` to keep, select “keep the local version currently installed”.
2020-12-07 10:03:03 -05:00
```shell
apt upgrade -y
```
2021-02-23 14:02:20 -05:00
### Step 23: reboot
```shell
systemctl reboot
```
### Step 24: log in
2020-12-07 10:03:03 -05:00
2021-02-23 14:02:20 -05:00
Replace `10.0.1.248` with IP of Raspberry Pi.
2021-04-04 07:26:38 -04:00
When asked for passphrase, enter passphrase from [step 1 ](#step-1-create-ssh-key-pair-on-macos ).
2020-12-07 10:03:03 -05:00
```shell
2021-02-23 14:02:20 -05:00
ssh pi@10 .0.1.248 -i ~/.ssh/pi
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 25: switch to root
2020-12-07 10:03:03 -05:00
```shell
2021-02-23 14:02:20 -05:00
sudo su -
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 26: set timezone (the following is for Montreal time)
2020-12-07 10:03:03 -05:00
2021-01-31 18:27:57 -05:00
See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
2020-12-07 10:03:03 -05:00
```shell
timedatectl set-timezone America/Montreal
```
2021-02-23 14:02:20 -05:00
### Step 27: configure sysctl (if network is IPv4-only)
2020-12-07 10:03:03 -05:00
2020-12-10 07:25:27 -05:00
> Heads-up: only run the following if network is IPv4-only.
2020-12-07 10:03:03 -05:00
```shell
cp /etc/sysctl.conf /etc/sysctl.conf.backup
cat < < "EOF" >> /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
EOF
sysctl -p
```
2021-02-23 14:02:20 -05:00
### Step 28: configure iptables
2020-12-07 10:03:03 -05:00
```shell
iptables -N SSH_BRUTE_FORCE_MITIGATION
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --set
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[ssh-brute-force]: "
iptables -A SSH_BRUTE_FORCE_MITIGATION -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
iptables -A SSH_BRUTE_FORCE_MITIGATION -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSH_BRUTE_FORCE_MITIGATION
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -P FORWARD DROP
iptables -P INPUT DROP
iptables -P OUTPUT DROP
```
2020-12-10 07:25:27 -05:00
If network is IPv4-only, run:
2020-12-07 10:03:03 -05:00
```shell
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
```
2020-12-10 07:25:27 -05:00
If network is dual stack (IPv4 + IPv6) run:
2020-12-07 10:03:03 -05:00
```shell
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p udp --dport 123 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT
ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -P FORWARD DROP
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
```
2021-02-23 14:02:20 -05:00
### Step 29: log out and log in to confirm iptables didn’ t block SSH
2020-12-07 10:03:03 -05:00
#### Log out
```shell
exit
exit
```
#### Log in
2020-12-10 08:16:05 -05:00
Replace `10.0.1.248` with IP of Raspberry Pi.
2020-12-07 10:03:03 -05:00
```shell
2020-12-10 08:16:05 -05:00
ssh pi@10 .0.1.248 -i ~/.ssh/pi
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 30: switch to root
2020-12-07 10:03:03 -05:00
```shell
2021-02-23 14:02:20 -05:00
sudo su -
2020-12-07 10:03:03 -05:00
```
2021-02-23 14:02:20 -05:00
### Step 31: make iptables rules persistent
2020-12-07 10:03:03 -05:00
```shell
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
```
👍