From b6b792380e6107a9d3ff6973ca8c2bd39465e179 Mon Sep 17 00:00:00 2001 From: Sun Knudsen Date: Mon, 7 Dec 2020 10:03:03 -0500 Subject: [PATCH] Added Raspberry Pi OS server guide --- .../README.md | 421 ++++++++++++++++++ .../README.md | 4 +- .../README.md | 30 +- 3 files changed, 438 insertions(+), 17 deletions(-) create mode 100644 how-to-configure-hardened-raspberry-pi-os-server/README.md diff --git a/how-to-configure-hardened-raspberry-pi-os-server/README.md b/how-to-configure-hardened-raspberry-pi-os-server/README.md new file mode 100644 index 0000000..a6226da --- /dev/null +++ b/how-to-configure-hardened-raspberry-pi-os-server/README.md @@ -0,0 +1,421 @@ + + +# How to configure hardened Raspberry Pi OS server + +## Requirements + +- [Raspberry Pi](https://www.raspberrypi.org/) +- Power adapter, keyboard and HDMI cable (and SD card reader if computer doesn’t have one built-in) +- Linux or macOS computer + +## 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 + +### Step 1: create SSH key pair (on computer) + +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). + +```shell +$ mkdir -p ~/.ssh + +$ 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 +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqEWjWQZY8HoLQEF7SmU/7f8dJuy/4r7/X6PbwrDzASym+5vCLz3QJIlLuJuQL557FsTzsz/sLFoTJnDkiQ3lZtFOofynyYQQiRDkZPy9nGz/j+rTHm6vfp17o8k1LeADKRq2mHpAljlf6/ywkKbzmGhXwnVsvxA0RvvNoujnpiLoSZFrq0wsfzIOi67ZzySlzxKmL0/Uxs9LJ+XFyKlVOILD+TMCayB6UNJ38PkghDZ/8spSSH44jkhmElPdqsqaJ/hqYYXT8q4sSB7Yp5RSYWDYIQiIs2cailho/gOCq/0AskREuGEuwPGL3ivOa2uBN544zrJHbegeliRwTwxSvFat9MoXjpWpEtTVpR5Skr2nGxwXZWYEGZ1u+iJc+Y67k7vabZYADAk1Q8VePAh2BOfhkhZfdAnBZJn9s/XISi/T8WxsLoG3twu1OMWtoiNMlwCUzeIzVRg5BMiFJjEtsANiETdrCuH/Ms9G/EMRo9gKh7moVbfwI+Urf0StIx3k= pi +``` + +### 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 +cat << _EOF > /home/pi/.ssh/authorized_keys +$(cat ~/.ssh/pi.pub) +_EOF +EOF +``` + +### Step 3: download latest version of [Raspberry Pi OS Lite](https://www.raspberrypi.org/software/operating-systems/) + +### Step 4: clone "Raspberry Pi OS Lite" to SD card + +> WARNING: DO NOT RUN THE FOLLOWING COMMANDS AS-IS. + +Run `diskutil list` to find disk ID of SD card to override with "Raspberry Pi OS Lite" (`disk2` in the following example). + +Replace `diskn` and `rdiskn` with disk ID of SD card (`disk2` and `rdisk2` in the following example) and `2020-12-02-raspios-buster-armhf-lite.img` with current image. + +```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 + +$ sudo dd bs=1m if=/Users/sunknudsen/Downloads/2020-12-02-raspios-buster-armhf-lite.img of=/dev/rdiskn +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` + +> Heads-up: default password is `raspberry`. + +### 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 + +Replace `10.0.1.69` with IP of Raspberry Pi. + +When asked for password, enter password from [step 5](#step-5-log-in-as-pi-using-keyboard-and-change-password-using-passwd). + +```shell +ssh pi@10.0.1.69 +``` + +### Step 10: configure pi SSH authorized keys + +#### Create `.ssh` folder + +```shell +mkdir -p /home/pi/.ssh +``` + +#### Create `/home/pi/.ssh/authorized_keys` using heredoc generated at [step 2](#step-2-generate-heredoc-the-output-of-following-command-will-be-used-at-step-10) + +```shell +cat << _EOF > /home/pi/.ssh/authorized_keys +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqEWjWQZY8HoLQEF7SmU/7f8dJuy/4r7/X6PbwrDzASym+5vCLz3QJIlLuJuQL557FsTzsz/sLFoTJnDkiQ3lZtFOofynyYQQiRDkZPy9nGz/j+rTHm6vfp17o8k1LeADKRq2mHpAljlf6/ywkKbzmGhXwnVsvxA0RvvNoujnpiLoSZFrq0wsfzIOi67ZzySlzxKmL0/Uxs9LJ+XFyKlVOILD+TMCayB6UNJ38PkghDZ/8spSSH44jkhmElPdqsqaJ/hqYYXT8q4sSB7Yp5RSYWDYIQiIs2cailho/gOCq/0AskREuGEuwPGL3ivOa2uBN544zrJHbegeliRwTwxSvFat9MoXjpWpEtTVpR5Skr2nGxwXZWYEGZ1u+iJc+Y67k7vabZYADAk1Q8VePAh2BOfhkhZfdAnBZJn9s/XISi/T8WxsLoG3twu1OMWtoiNMlwCUzeIzVRg5BMiFJjEtsANiETdrCuH/Ms9G/EMRo9gKh7moVbfwI+Urf0StIx3k= pi +_EOF +``` + +### Step 11: log out + +```shell +exit +``` + +### Step 12: log in to Raspberry Pi over SSH (using `pi` private key) + +Replace `10.0.1.69` with IP of Raspberry Pi. + +When asked for password, enter password from [step 1](#step-1-create-ssh-key-pair-on-computer). + +```shell +ssh pi@10.0.1.69 -i ~/.ssh/pi +``` + +### 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 +source ~/.bashrc +``` + +### Step 14: switch to root + +```shell +sudo su - +``` + +### Step 15: disable root Bash history + +```shell +echo "HISTFILESIZE=0" >> ~/.bashrc +source ~/.bashrc +``` + +### Step 16: set root password + +When asked for password, use output from `openssl rand -base64 24` (and store password in password manager). + +```shell +passwd +``` + +### Step 17: uninstall sudo + +```shell +apt remove -y sudo +``` + +### Step 18: disable root login and password authentication + +```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 +``` + +### Step 19: disable Bluetooth and Wi-Fi + +> Heads-up: will take effect after reboot. + +#### Disable Bluetooth + +```shell +echo "dtoverlay=disable-bt" | tee -a /boot/config.txt +systemctl disable hciuart +``` + +#### Disable Wi-Fi (if using ethernet) + +```shell +echo "dtoverlay=disable-wifi" | tee -a /boot/config.txt +``` + +### Step 20: update APT index and upgrade packages + +#### Update APT index + +```shell +apt update +``` + +#### Upgrade packages + +```shell +apt upgrade -y +``` + +### Step 21: install and configure Vim + +#### Install Vim + +```shell +apt install -y vim +``` + +#### Configure Vim + +```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 +``` + +### Step 22: set timezone (the following is for Montreal time) + +See [https://en.wikipedia.org/wiki/List_of_tz_database_time_zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for available timezones. + +```shell +timedatectl set-timezone America/Montreal +``` + +### Step 23: configure sysctl (if server is IPv4-only) + +> Heads-up: only run the following if server is IPv4-only. + +```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 +``` + +### Step 24: install iptables-persistent + +When asked to save current IPv4 or IPv6 rules, answer `Yes`. + +```shell +apt install -y iptables-persistent +``` + +### Step 25: configure iptables + +```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 +``` + +If server is IPv4-only, run: + +```shell +ip6tables -P FORWARD DROP +ip6tables -P INPUT DROP +ip6tables -P OUTPUT DROP +``` + +If server is dual stack (IPv4 + IPv6) run: + +```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 +``` + +### Step 26: log out and log in to confirm iptables didn’t block SSH + +#### Log out + +```shell +exit +exit +``` + +#### Log in + +Replace `10.0.1.69` with IP of Raspberry Pi. + +When asked for password, enter password from [step 1](#step-1-create-ssh-key-pair-on-computer). + +```shell +ssh pi@10.0.1.69 -i ~/.ssh/pi +``` + +#### Switch to root + +When asked, enter root password. + +```shell +su - +``` + +### Step 27: make iptables rules persistent + +```shell +iptables-save > /etc/iptables/rules.v4 +ip6tables-save > /etc/iptables/rules.v6 +``` + +👍 diff --git a/how-to-self-host-hardened-borg-server/README.md b/how-to-self-host-hardened-borg-server/README.md index a54b9d0..8277755 100644 --- a/how-to-self-host-hardened-borg-server/README.md +++ b/how-to-self-host-hardened-borg-server/README.md @@ -12,7 +12,7 @@ Listed: true ## Requirements -- [Hardened Debian server](../how-to-configure-hardened-debian-server) 📦 +- [Hardened Debian server](../how-to-configure-hardened-debian-server) 📦 or [hardened Raspberry Pi OS server](../how-to-configure-hardened-raspberry-pi-os-server) 📦 - Linux or macOS computer ## Caveats @@ -113,7 +113,7 @@ EOF ### Step 4: log in to server -Replace `185.112.147.115` with IP of server. +Replace `server-admin@185.112.147.115` with SSH destination of server and `~/.ssh/server` with path to associated private key. ```shell ssh server-admin@185.112.147.115 -i ~/.ssh/server diff --git a/how-to-self-host-hardened-strongswan-ikev2-ipsec-vpn-server-for-ios-and-macos/README.md b/how-to-self-host-hardened-strongswan-ikev2-ipsec-vpn-server-for-ios-and-macos/README.md index 742d8c4..df424d7 100644 --- a/how-to-self-host-hardened-strongswan-ikev2-ipsec-vpn-server-for-ios-and-macos/README.md +++ b/how-to-self-host-hardened-strongswan-ikev2-ipsec-vpn-server-for-ios-and-macos/README.md @@ -427,7 +427,7 @@ systemctl restart dnsmasq ### Step 23: install strongSwan -If you are shown an “Old runlevel management superseded” warning, answer `Ok`. +If you are shown an “Old runlevel management superseded” warning, answer “Ok”. ```shell apt install -y strongswan libcharon-extra-plugins @@ -595,7 +595,7 @@ EOF cd /etc/strongswan.d/charon sed -i 's/load = yes/load = no/' ./*.conf sed -i 's/load = no/load = yes/' ./eap-tls.conf ./aes.conf ./dhcp.conf ./farp.conf ./gcm.conf ./hmac.conf ./kernel-netlink.conf ./nonce.conf ./openssl.conf ./pem.conf ./pgp.conf ./pkcs12.conf ./pkcs7.conf ./pkcs8.conf ./pubkey.conf ./random.conf ./revocation.conf ./sha2.conf ./socket-default.conf ./stroke.conf ./x509.conf -cd +cd - ``` #### Backup and edit `/lib/systemd/system/strongswan.service` @@ -624,7 +624,7 @@ cd ~/Desktop/strongswan-certs Each client is configured using a unique common name ending with `@vpn-server.com`. ```shell -STRONGSWAN_CLIENT_COMMON_NAME=john@vpn-server.com +STRONGSWAN_CLIENT_COMMON_NAME=alice@vpn-server.com ``` #### Create OpenSSL config file @@ -696,25 +696,25 @@ Getting CA Private Key When asked for export password, use output from `openssl rand -base64 24` (and store password in password manager). ```console -$ openssl genrsa -out john.key 4096 +$ openssl genrsa -out alice.key 4096 Generating RSA private key, 4096 bit long modulus .........++ ............................................................................++ e is 65537 (0x10001) -$ openssl req -new -config openssl.cnf -extensions client -key john.key -subj "/C=US/O=Self-hosted strongSwan VPN/CN=$STRONGSWAN_CLIENT_COMMON_NAME" -out john.csr +$ openssl req -new -config openssl.cnf -extensions client -key alice.key -subj "/C=US/O=Self-hosted strongSwan VPN/CN=$STRONGSWAN_CLIENT_COMMON_NAME" -out alice.csr -$ openssl x509 -req -extfile openssl.cnf -extensions client -in john.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out john.crt +$ openssl x509 -req -extfile openssl.cnf -extensions client -in alice.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out alice.crt Signature ok -subject=/C=US/O=Self-hosted strongSwan VPN/CN=john@vpn-server.com +subject=/C=US/O=Self-hosted strongSwan VPN/CN=alice@vpn-server.com Getting CA Private Key -$ openssl pkcs12 -in john.crt -inkey john.key -certfile ca.crt -export -out john.p12 +$ openssl pkcs12 -in alice.crt -inkey alice.key -certfile ca.crt -export -out alice.p12 Enter Export Password: Verifying - Enter Export Password: ``` -### Step 30: copy/paste the content of `ca.crt`, `server.key` and `server.crt` to server and make private key root-only. +### Step 30: copy/paste content of `ca.crt`, `server.key` and `server.crt` to server and make private key root-only. On Mac: run `cat ca.crt` @@ -779,7 +779,7 @@ In “General”, enter “Self-hosted strongSwan VPN” in “Name”. ![apple-configurator-general](apple-configurator-general.png?shadow=1) -In “Certificates”, click “Configure” and select “ca.crt”. Then click “+” and select “john.p12”. The password is the one from [step 29](#step-29-generate-client-cert). +In “Certificates”, click “Configure” and select “ca.crt”. Then click “+” and select “alice.p12”. The password is the one from [step 29](#step-29-generate-client-cert). ![apple-configurator-certificates](apple-configurator-certificates.png?shadow=1) @@ -789,7 +789,7 @@ The “Child SA Params” are the same as “IKE SA Params”. ![apple-configurator-vpn](apple-configurator-vpn.png?shadow=1) -Finally, click “File”, then “Save”, and save file as “john.mobileconfig”. +Finally, click “File”, then “Save”, and save file as “alice.mobileconfig”. ### Step 34: add VPN profile to iPhone using Apple Configurator 2 @@ -797,24 +797,24 @@ Unlock iPhone, connect it to Mac using USB cable and open Apple Configurator 2. In “All Devices”, double-click on iPhone, then “Add”, and finally “Profiles”. -Select “john.mobileconfig” and follow instructions. +Select “alice.mobileconfig” and follow instructions. On iPhone, open “Settings”, then “Profile Downloaded” and tap “Install”. **If this steps fails (a recent update to Apple Configurator 2 has introduced a bug), please run the following and try again.** ```shell -sed -i '' '/DNS<\/key>/,/<\/dict>/d' ~/Desktop/strongswan-certs/john.mobileconfig +sed -i '' '/DNS<\/key>/,/<\/dict>/d' ~/Desktop/strongswan-certs/alice.mobileconfig ``` ### Step 35: add VPN profile to Mac -This step is super simple, simply double-click “john.mobileconfig” and follow instructions. +This step is super simple, simply double-click “alice.mobileconfig” and follow instructions. **If this steps fails (a recent update to Apple Configurator 2 has introduced a bug), please run the following and try again.** ```shell -sed -i '' '/DNS<\/key>/,/<\/dict>/d' ~/Desktop/strongswan-certs/john.mobileconfig +sed -i '' '/DNS<\/key>/,/<\/dict>/d' ~/Desktop/strongswan-certs/alice.mobileconfig ``` ### Step 36: connect to VPN on iPhone or Mac