Skip to main content

RKways tech blog

Raspberry Pi Vault

Photo by Mike Szczepanski on Unsplash

Provisioning systems with PXE network boot typically involves wide-open TFTP server yet a fresh OS could use some secret keys to e.g. join existing Kubernetes cluster. Some platforms may utilise built-in TPM module to decrypt a bit of data but it’s unavailable on Raspberry Pi 4. Fortunately, the EEPROM can be (ab)used to store a per-machine or per-cluster key which later will decrypt other secrets with tools like GnuPG or OpenSSL.

Raspberry Pi 4 EEPROM

The boot configuration EEPROM looks like:

$ vcgencmd bootloader_config
[all]
BOOT_UART=0
WAKE_ON_GPIO=1
...

It’s an ini-file with textual key-value pairs. Unknown keys are ignored by the firmware, so a custom one can be used to store a small secret as long as it’s hex encoded.

The rpi-eeprom-update(1) and rpi-eeprom-config(1) man pages describe how to change the boot configuration parameters. A short version follows but please verify that the correct firmware file is used:

$ vcgencmd bootloader_version
Apr 16 2020 18:11:26
version a5e1b95f320810c69441557c5f5f0a7f2460dfb8 (release)
timestamp 1587057086

Obtaining the EEPROM file:

. /etc/default/rpi-eeprom-update
EEPROM_FILENAME=pieeprom-$(date -d "$(vcgencmd bootloader_version | head -n 1)" +%Y-%m-%d).bin
EEPROM_PATH="/lib/firmware/raspberrypi/bootloader/$FIRMWARE_RELEASE_STATUS/$EEPROM_FILENAME"
test -f "$EEPROM_PATH" || { echo "No eeprom file found"; exit 1; }
cp "$EEPROM_PATH" pieeprom.bin

The bootloader configuration could be extracted from

  • pieeprom.bin file:

    rpi-eeprom-config pieeprom.bin --out bootconf.txt
    
  • the EEPROM chip:

    vcgencmd bootloader_config > bootconf.txt
    

To add the custom secret just put a line like SECRET_STR=aabbccdd to the end of [all] section. Replace the value with e.g. output of sha512sum command fed with random data. Example:

$ vcgencmd bootloader_config
[all]
BOOT_UART=0
...
NET_BOOT_MAX_RETRIES=2
SECRET_STR=952de772210118f043a4e2225da5f5943609c653a6736940e0fad4e9c7cd3cfdd348abebbf28af7b4438c55515e5a351b87cc60c808673f4d23cf12237debf41
[none]
...

Flash it to the EEPROM:

rpi-eeprom-config pieeprom.bin --config bootconf.txt --out pieeprom-new.bin
sudo rpi-eeprom-update -d -f ./pieeprom-new.bin

Notice the “EEPROM update pending. Please reboot to apply the update.” message.

Cleaning up:

read -N 1 -p "Remove temporary files? [y/N]"
[ "$REPLY" != "y" ] || rm -f pieeprom.bin bootconf.txt pieeprom-new.bin

The Vault

  • Secrets encryption

    Let’s assume that plaintext_file holds shell variables that should be confidential like K3s Node Token.

    gpg --symmetric --cipher-algo AES256 plaintext_file
    

    The resulting *.gpg file is encrypted with 256-bit AES symmetric cipher and can be safely stored on a TFTP server.

  • Extracting the EEPROM key:

    EEPROM_KEY=$(vcgencmd bootloader_config | grep -e "^SECRET_STR=" | cut -d= -f2)
    
  • Decrypting secrets

    umask 0077
    GNUPGHOME=/root
    { echo $EEPROM_KEY; cat /etc/rpi-vault/k3s_env.gpg; } | \
        gpg --no-options --batch --passphrase-fd 0 -o /etc/rancher/k3s/k3s_env -d -
    

Security

The boot configuration memory is accessible to the root user or any member of video group on Raspberry Pi OS. The vcgencmd command uses /dev/vchiq character device:

$ ls -l /dev/vchiq
crw-rw---- 1 root video 242, 0 Jun 10 22:18 /dev/vchiq