• Stars
    star
    130
  • Rank 268,175 (Top 6 %)
  • Language
  • Created over 4 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🔑 Raspberry Pi Encrypted Boot with Remote SSH

Raspberry Pi Encrypted Boot with SSH

⚠️ This guide is only supported for Raspberry Pi 3B & 4B with Ubuntu Server 22.04 and Raspberry Pi OS Lite 11 (5.15).
Other platforms and distributions may work, but there will be unexpected issues or side effects.

Introduction

This guide will show you how to encrypt your Raspberry Pi's root partition and set up an initramfs that will prompt for the password, decrypt the partition and gracefully resume boot. You will also learn how to enable SSH during this pre-boot stage, allowing you to unlock the partition remotely. There are also optional steps for WiFi setup.

While the steps are written for the Raspberry Pi, they should be easily transferrable to other SBCs and computers as a whole. However, only the Raspberry Pi is officially supported by this guide.

This guide operates directly on an image file and therefore does not require an SD card for the setup. The resulting image can be flashed to an SD card as usual.

Table of Content

Requirements

  • A Raspberry Pi Linux image (e.g. Ubuntu Server 22.04 or Raspberry Pi OS Lite 11 (5.15))

  • A computer (host) running Linux (e.g. Xubuntu 22.04)

    ⚠️ NOTE: Your host's Linux should be as similar as possible to the Raspberry Pi's Linux. If you are preparing Ubuntu 22.04 for the Raspberry Pi, use the same version on the host, otherwise you may encounter issues inside the chroot.

On the host

Install dependencies:

  • You can skip qemu-user-static if your host Linux's architecture matches that of the Raspberry Pi's Linux image.
apt update
apt install -y kpartx cryptsetup-bin qemu-user-static

Create two copies of the Raspberry Pi's Linux image — one to read from (base), and one to write to (target):

  • pi-base.img
  • pi-target.img

If you're planning to install additional software (e.g. WiFi drivers), increase the size of the target image or you may not have enough space:

dd if=/dev/zero bs=1G count=1 >> pi-target.img
parted pi-target.img resizepart 2 100%

Map both images as devices, ensuring the base is readonly:

kpartx -ar "$PWD/pi-base.img"
kpartx -a "$PWD/pi-target.img"

If your system automatically mounted any partitions, unmount them:

umount /media/**/*

Run lsblk and verify the process was successful — you should see two loopback devices, each with two partitions:

NAME      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT # COMMENT
loop0       7:0    0  3.3G  0 loop            # pi-base.img
├─loop0p1 253:0    0  256M  0 part            # ├─ boot
└─loop0p2 253:1    0    3G  0 part            # └─ root
loop1       7:1    0  3.3G  1 loop            # pi-target.img
├─loop1p1 253:2    0  256M  1 part            # ├─ boot
└─loop1p2 253:3    0    3G  1 part            # └─ root

Mount the base image's root partition:

mkdir -p /mnt/original/
mount /dev/mapper/loop0p2 /mnt/original/

Replace the target image's root partition with a new, encrypted partition:

⚠️ NOTE:

In this example we will use aes-adiantum as the encryption method since it is much faster on targets that lack hardware AES acceleration. Ensure that both the host's and Pi's kernel (>= 5.0.0, must include .ko) and cryptsetup (>= 2.0.6) support your encryption method.

By default cryptsetup will use a memory-hard PBKDF algorithm that requires 4GB of RAM. With these settings, you are likely to exceed the Raspberry Pi's maximum RAM and make it impossible to unlock the partition. To work around this, set the --pbkdf-memory and --pbkdf-parallel arguments so when you multiply them, the result is less than your Pi's total RAM:

cryptsetup luksFormat -c xchacha20,aes-adiantum-plain64 --pbkdf-memory 512000 --pbkdf-parallel=1 /dev/mapper/loop1p2

Open (decrypt) the new partition:

cryptsetup open /dev/mapper/loop1p2 crypted

Then format and mount it:

mkfs.ext4 /dev/mapper/crypted
mkdir -p /mnt/chroot/
mount /dev/mapper/crypted /mnt/chroot/

Copy the base image's root partition files to the target image's new, encrypted root partition. You can use dd, but rsync is faster:

rsync --archive --hard-links --acls --xattrs --one-file-system --numeric-ids --info="progress2" /mnt/original/* /mnt/chroot/

Set up a chroot by mounting the target image's boot partition and required virtual filesystems from the host:

mkdir -p /mnt/chroot/boot/
mount /dev/mapper/loop1p1 /mnt/chroot/boot/
mount -t proc none /mnt/chroot/proc/
mount -t sysfs none /mnt/chroot/sys/
mount -o bind /dev /mnt/chroot/dev/
mount -o bind /dev/pts /mnt/chroot/dev/pts/

Enter the chroot:

LANG=C chroot /mnt/chroot/

In the chroot

Prepare

Since Ubuntu has a symlinked resolv.conf that is invalid in the chroot context, you will not have internet access. To work around this, back it up and create a simple nameserver replacement:

mv /etc/resolv.conf /etc/resolv.conf.bak
echo "nameserver 1.1.1.1" > /etc/resolv.conf

Next, install the dependencies:

apt update
apt install -y busybox cryptsetup dropbear-initramfs

Device configuration

Edit /etc/fstab and replace the root entry with your decrypted (virtual) partition's device name:

#PARTUUID=e8af6eb2-02 / ext4 defaults,noatime          0 1
#LABEL=writable	      /	ext4 discard,errors=remount-ro 0 1
/dev/mapper/crypted   / ext4 defaults,noatime          0 1

Run blkid and note the details of your encrypted partition:

blkid | grep crypto_LUKS

/dev/mapper/loop1p2: UUID="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" TYPE="crypto_LUKS" PARTUUID="cccccccc-cc"

Edit /etc/crypttab and add an entry with your encrypted (raw) partition's UUID:

crypted UUID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa none luks,initramfs

Edit /boot/cmdline.txt and update the root entry:

#root=PARTUUID=21e60f8c-02
#root=/dev/mmcblk0p2
root=/dev/mapper/crypted cryptdevice=UUID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:crypted

Enable SSH for the decrypted OS:

touch /boot/ssh

Cryptsetup

Edit the cryptsetup initramfs hook to ensure cryptsetup ends up in the initramfs:

echo "CRYPTSETUP=y" >> /etc/cryptsetup-initramfs/conf-hook

The initramfs-tools cryptroot hook will resolve any UUIDs to device names during initramfs generation. This is a problem because the device names will likely differ between the host and the Raspberry Pi, resulting in failure to boot. To work around this, apply the following patch:

patch --no-backup-if-mismatch /usr/share/initramfs-tools/hooks/cryptroot << 'EOF'
--- cryptroot
+++ cryptroot
@@ -33,7 +33,7 @@
         printf '%s\0' "$target" >>"$DESTDIR/cryptroot/targets"
         crypttab_find_entry "$target" || return 1
         crypttab_parse_options --missing-path=warn || return 1
-        crypttab_print_entry
+        printf '%s %s %s %s\n' "$_CRYPTTAB_NAME" "$_CRYPTTAB_SOURCE" "$_CRYPTTAB_KEY" "$_CRYPTTAB_OPTIONS" >&3
     fi
 }
EOF

The default timeout when waiting for decryption (10 seconds) may be too short and result in a timeout error. To work around this, bump the value:

sed -i 's/^TIMEOUT=.*/TIMEOUT=100/g' /usr/share/cryptsetup/initramfs/bin/cryptroot-unlock

SSH

Write your SSH public key inside dropbear's and your decrypted OS's authorized_keys and fix permissions:

mkdir -p /root/.ssh && chmod 0700 /root/.ssh
# Ubuntu
echo "/REDACTED/" | tee /etc/dropbear/initramfs/authorized_keys /root/.ssh/authorized_keys
chmod 0600 /etc/dropbear/initramfs/authorized_keys /root/.ssh/authorized_keys
# Raspberry Pi OS
echo "/REDACTED/" | tee /etc/dropbear-initramfs/authorized_keys /root/.ssh/authorized_keys
chmod 0600 /etc/dropbear-initramfs/authorized_keys /root/.ssh/authorized_keys

WiFi support

This step is optional. If you want the Raspberry Pi to be decryptable over WiFi, check out the guides below. Note that the differences between distros is very small, so you can easily adapt any particular guide.

Ubuntu

Raspberry Pi OS

Build initramfs

Ubuntu

Note your kernel version. If there are multiple, choose the one you want to run:

ls /lib/modules/

Build the new initramdisk using the kernel version from above, overwriting the old initramdisk:

mkinitramfs -o /boot/initrd.img "5.15.0-1005-raspi"

Raspberry Pi OS

Enable automatic initramfs generation on kernel update:

sed -i 's/^#INITRD=Yes$/INITRD=Yes/g' /etc/default/raspberrypi-kernel

This will create a differently suffixed file on every update. To make your Raspberry boot from the latest one every time, create the following file:

  • /etc/initramfs-tools/hooks/update_initrd

    #!/bin/sh -e
    # Update reference to $INITRD in $BOOTCFG, making the kernel use the new
    # initrd after the next reboot.
    BOOTLDR_DIR=/boot
    BOOTCFG=$BOOTLDR_DIR/config.txt
    INITRD_PFX=initrd.img-
    INITRD=$INITRD_PFX$version
    
    case $1 in
        prereqs) echo; exit
    esac
    
    FROM="^ *\\(initramfs\\) \\+$INITRD_PFX.\\+ \\+\\(followkernel\\) *\$"
    INTO="\\1 $INITRD \\2"
    
    T=`umask 077 && mktemp --tmpdir genramfs_XXXXXXXXXX.tmp`
    trap "rm -- \"$T\"" 0
    
    sed "s/$FROM/$INTO/" "$BOOTCFG" > "$T"
    
    # Update file only if necessary.
    if ! cmp -s "$BOOTCFG" "$T"
    then
        cat "$T" > "$BOOTCFG"
    fi

Then make it executable:

chmod +x /etc/initramfs-tools/hooks/update_initrd

Note your kernel version. If there are multiple, choose the one you want to run:

ls /lib/modules/

Build the new initramdisk using the kernel version from above, and make the Raspberry boot from this ramdisk:

mkinitramfs -o /boot/initrd.img-5.15.61-v8+ "5.15.61-v8+"
echo "initramfs initrd.img-5.15.61-v8+ followkernel" >> /boot/config.txt

Customize headless setup and first run optimizations as they will error with and prevent booting:

sed -i 's/^main$/fix_wpa;regenerate_ssh_host_keys/g' /usr/lib/raspberrypi-sys-mods/firstboot
echo "pi:$6$Gpq1Y5a26F7cPIuL$VeIz04vCAZFE6RfFnH.BInFyiHp.pylFKzLYoVfDav1dCYAeUJqISZngIaQNcdr1SJfJWXbmBk7DftioULVYW0" > /boot/userconf.txt

Cleanup

Revert any changes if you have made them before:

mv /etc/resolv.conf.bak /etc/resolv.conf

Sync and exit the chroot:

sync
history -c && exit

On the host

Unmount everything and clean up any remaining artifacts:

umount /mnt/chroot/boot
umount /mnt/chroot/sys
umount /mnt/chroot/proc
umount /mnt/chroot/dev/pts
umount /mnt/chroot/dev
umount /mnt/chroot
cryptsetup close crypted
umount /mnt/original
rm -d /mnt/chroot
rm -d /mnt/original
kpartx -d "$PWD/pi-base.img"
kpartx -d "$PWD/pi-target.img"

You are now ready to flash pi-target.img to an SD card.

On the Raspberry Pi

Boot the Raspberry Pi with the new SD card. It will obtain an IP address from the DHCP server and start listening for SSH connections. To decrypt the root partition and continue boot, from any shell, simply run cryptroot-unlock.

Once booted into the decrypted system, you will notice that the root partition is still sized at ~3GB, no matter how much space you have on the SD card. To fix this, resize the partition:

parted /dev/mmcblk0 resizepart 2 100%
cryptsetup resize crypted
resize2fs /dev/mapper/crypted

Finally, reboot the system for good measure:

reboot

Avoiding SSH key collisions

To avoid host key collisions you can configure a separate trusted hosts store in the ~/.ssh/config of your client:

Host box
	Hostname 192.168.0.30
	User root

Host box-initramfs
	Hostname 192.168.0.30
	User root
	UserKnownHostsFile ~/.ssh/known_hosts.initramfs

Resources

More Repositories

1

wgcf

🚤 Cross-platform, unofficial CLI for Cloudflare Warp
Go
2,850
star
2

TrustMeAlready

🔓 Disable SSL verification and pinning on Android, system-wide
Java
967
star
3

magisk-frida

🔐 Run frida-server on boot with Magisk, always up-to-date
Shell
495
star
4

de4dot-cex

📦 de4dot deobfuscator with full support for vanilla ConfuserEx
C#
387
star
5

apk-utilities

🛠 Tools and scripts to manipulate Android APKs
Python
186
star
6

LenovoController

🎮 A lightweight alternative to Lenovo Vantage
C#
164
star
7

cloudflare-warp-wireguard-client

Generate WireGuard profile from Cloudflare Warp account
Python
132
star
8

dnEditor

💾 A .NET assembly editor based on dnlib
C#
122
star
9

jvm-dump-proxy

A proxy DLL for Windows to dump JVM classes at JNI level
C
61
star
10

nMAC

📱 An Android device MAC changer
C#
56
star
11

z3-python-ctf

🐍 Solving CTF challenges using Z3 and Python
Python
24
star
12

poweruser

💪 Collection of tips, tricks and scripts for becoming a true poweruser
Shell
22
star
13

authelia-basic-2fa

🗝 Use Authelia 2FA through only standard basic auth
Go
18
star
14

avast-ctf-cambridge-2018

🎖 A complete write-up of the Avast challenge given at Hack Cambridge 2018
18
star
15

nginx-gotchas

⁉ My personal list of gotchas and other tricky situations with nginx
15
star
16

SmaliHelper

An offline documentation browser for Smali
C#
14
star
17

wg-reverse-proxy

🛤 Ansible setup for building a WireGuard reverse proxy server
Shell
14
star
18

webdav-server

A simple WebDAV server in Go
Go
11
star
19

tailscale-bolt

Create self-contained, 1-click scripts, which connect any computer to your TailScale network
Shell
11
star
20

brookshear-vm

A brookshear virtual machine written in Go
Go
11
star
21

PerfectProxyDLL

A simple and stable proxy DLL for Windows x64
C
10
star
22

ProcessPrivileges

Query, enable, disable or remove privileges on a process
C#
9
star
23

CompatTelOverride

Persistently disable the Compatibility Telemetry in Windows 10
C#
8
star
24

RunInTray

Run a program in tray with its own icon
C#
7
star
25

scanogram

Scan your pictures and videos for corruption, and sort them by EXIF or modification time
Go
7
star
26

BinaryPatcher

A library to patch binaries
C#
7
star
27

proxy-redirector

A HTTP/S proxy that redirects connections
Go
7
star
28

windows-ansible

Ansible playbook to configure a Windows machine
PowerShell
7
star
29

anvil-calc

Optimal enchantment order calculator for modded Minecraft
Rust
6
star
30

pwnEd-ctf

Write-ups for pwnEd CTF in Edinburgh 2020
Python
6
star
31

WhiteScrolls

📃 Articles, papers and presentations
6
star
32

http-ci-deployer

An extremely simple HTTP-based deployment solution for Continuous Integration services
Go
5
star
33

docker-elk-arm64

ELK stack for Docker ARM64
Shell
5
star
34

devpost-api

📜 An unofficial API for DevPost
Python
4
star
35

simplewall-utils

Simple utilities for simplewall
Go
4
star
36

drunk-sshd

🍺 Byte replace patterns to make sshd accept any password as valid
4
star
37

winsock-dump-proxy

A proxy DLL for Windows to dump Windows Socket connections
C
4
star
38

overseer

Search an Android device for files containing its MAC address
C
3
star
39

android-kernel-ci

Generic auto-build repo for Android kernels using various CI
Shell
3
star
40

serve-zip

Simple HTTP server that streams directories as a ZIP
XSLT
3
star
41

byte-pattern-patcher

A minimalistic, performant, cross-platform byte pattern patcher
Go
3
star
42

sdconv

Automatic SD video conversion script
Python
2
star
43

text2pic

A text to picture translator understandable by anyone
C#
2
star
44

stirling-workshop-2

The materials for the Raspberry Pi workshop
Shell
2
star
45

sigint-workshop-1

The materials for the Dissecting Android Apps workshop
2
star
46

iddqdBot

A PvE bot for the RESTful DOOM engine
Java
2
star
47

TimetableManager

A simple timetable manager, written for a university assignment project
Java
2
star
48

jre-side-channel

Various JRE patches to aid side-channel analysis in Java
Java
2
star
49

stirling-workshop-1

The materials for the Introduction to Cyber Security workshop
2
star
50

cdx2018-ransomware-decryptor

CDX 2018 Ransomware Decryptor
Go
2
star
51

upscale-win

Workflow for upscaling and interpolating videos with NVIDIA TensorRT acceleration
Python
2
star
52

handbraked

Watch and convert videos in a directory using Handbrake
Go
2
star
53

vscode-essay

A VSCode template for HTML and PDF generation from Markdown with GitHub-style theme and extras
CSS
2
star
54

APlusGenerator

A code generator for the APlus project
C#
2
star
55

gomobiler

A stripped-down gomobile to easily build go executables for Android
Go
2
star
56

APlus

An application aiming to create truly anonymous school examination sheets while keeping things simple
C#
2
star
57

z3-wasm

Run Z3 entirely in your browser using WebAssembly
JavaScript
1
star
58

tcp-packet-parser

A PCAP analyzer that filters TCP packets and dumps them with appropriate labels
Go
1
star
59

docker-windows-process-isolation

🛳 Install Docker in process isolation mode on Windows
PowerShell
1
star
60

gitlab-gitea-migrator

Batch repository migrator from GitLab to Gitea using their APIs
Python
1
star
61

APlusServer

The server part of the APlus project
PHP
1
star
62

ieReplace

Replace Internet Explorer 11 with a stub that redirects URLs to Edge
C++
1
star
63

human-bot

A bot for МSTanks with a twist
Python
1
star
64

vscode-smali

Smali syntax highlighting for VS Code
CoffeeScript
1
star
65

NaniteProtector-demo

A demo of the Nanite Protector for Java
1
star
66

optic-go

Utilities for API generation using Optic in Go
Go
1
star
67

jekyll-bootstrap-mermaid-template

A clean and simple Jekyll + Bootstrap 4 + Mermaid template
SCSS
1
star
68

MCProtocolLib

A library for communication with a Minecraft client/server.
Java
1
star
69

docker-windows-patcher

🚢 A memory patch to workaround Windows bug with Docker
C++
1
star