Как говорила моя бабушка - причудливой инфре причудливые решения. Так до недавнего времени у меня жил VPN-gateway на RPi3. Жил, потому что нежданно-негаданно малинка померла, и коль заменить RPi3 на что-то другое я хотел больше, чем заниматься его ремонтом - пришлось собрать новый :)
А раз уж подвернулся такой повод, будем ставить Armbian с Full Disk Encryption (FDE), опциональным Network-Bound Disk Encryption (NBDE) и поддержкой Trusted Platform Module (TPM). Но обо всем по порядку, не переключайтесь.
О железках
В качестве SBC мой выбор пал на Orange Pi Zero 3, причин тому несколько:
- достаточно производительный для моих текущих запросов
- с гигабитной сетевухой на борту
- не малинка
- лежал без дела в столе, грешновато как ни крути ;)
- просто красавчик размером 50x55mm:
И OPTIGA™ SLB 9670 от Infineon в роли TPM2.0 чипа, конкретно у меня в исполнении для материнок MSI:
Вместо покупки готового модуля можно было бы собрать и самому (на JLCPCB даже SLB9670VQ12FW643XUMA2 + монтаж можно заказать, а не только печатную плату), но мне было лениво, а выгоды по деньгам (при штучном производстве офк) никакой. Да и мелкий колхоз как-то ламповее что-ли, чего скрывать :)
Если говорить о том, почему я вообще взял SLB9670, то тут тоже без открытий:
- дешевый и популярный
- без проблем отработал у меня пару лет от звонка до звонка в исполнении GeeekPi TPM2.0 Module for Raspberry Pi
- есть in-kernel драйвер
Готовим Armbian
Я знаю два способа получить FDE для Armbian:
- сделать все руками из готового образа
- собрать образ самостоятельно с включенным FDE
Сборка Armbian процесс не хитрый, грех не воспользоваться. Для всяких подобных штук у меня есть выделенная машинка, потому топаю на неё и собираю Armbian для Orange Pi Zero 3:
- клонирую репо и переключаюсь на стабильную версию (люблю иметь полную историю):
% git clone https://github.com/armbian/build armbian-build
% cd armbian-build
% git checkout v24.11.1
Previous HEAD position was 270da9d96 sunxi-6.6: Fix incomplete: add community support for LonganPi 3H (#7547)
HEAD is now at dd379da88 Release: set version and froze sources
- запускаю сборку:
% ./compile.sh \
CRYPTROOT_ENABLE=yes \
CRYPTROOT_PASSPHRASE=changeme \
CRYPTROOT_SSH_UNLOCK=yes \
CRYPTROOT_SSH_UNLOCK_PORT=2222
- значения флажков можно посмотреть в Armbian: Build Switches, но они выглядят очевидными:
CRYPTROOT_ENABLE=yes
- включает шифрование rootfsCRYPTROOT_PASSPHRASE=changeme
- задаетpassphrase
для шифрования, но я на 100% уверен, что он останется в каких-нить логах, потому проще сменить послеCRYPTROOT_SSH_UNLOCK
- включаетdropbear-initramfs
для анлока диска по SSHCRYPTROOT_SSH_UNLOCK_PORT=2222
- порт, на котором будет слушатьdropbear
- по окончании стягиваю к себе:
- получившийся образ:
Armbian-unofficial_24.11.1_Orangepizero3_bookworm_current_6.6.60-crypt.img
- SSH ключ для похода в dropbear (изменим его позже):
Armbian-unofficial_24.11.1_Orangepizero3_bookworm_current_.key
- получившийся образ:
- монтирую образ:
# kpartx -v -a Armbian-unofficial_24.11.1_Orangepizero3_bookworm_current_6.6.60-crypt.img
add map loop0p1 (254:1): 0 524288 linear 7:0 8192
add map loop0p2 (254:2): 0 4194304 linear 7:0 532480
- добавляю новый
passphrase
:
# cryptsetup luksAddKey /dev/mapper/loop0p2
Enter any existing passphrase:
Enter new passphrase for key slot:
Verify passphrase:
- удаляю старый:
# cryptsetup luksRemoveKey /dev/mapper/loop0p2
Enter passphrase to be deleted:
- на всякий случай меняю master ключ (не уверен в необходимости, но хуже не будет):
# cryptsetup reencrypt /dev/mapper/loop0p2
Enter passphrase for key slot 1:
Finished, time 00m04s, 2032 MiB written, speed 431.1 MiB/s
- размонтирую образ:
# kpartx -d Armbian-unofficial_24.11.1_Orangepizero3_bookworm_current_6.6.60-crypt.img
- и заливаю его на флешку:
# dd if=Armbian-unofficial_24.11.1_Orangepizero3_bookworm_current_6.6.60-crypt.img of=/dev/sdb bs=1M status=progress
2413821952 bytes (2.4 GB, 2.2 GiB) copied, 75 s, 32.2 MB/s
2308+0 records in
2308+0 records out
2420113408 bytes (2.4 GB, 2.3 GiB) copied, 111.504 s, 21.7 MB/s
# sync
На этом подготовка окончена – можно вставлять флешку, подключать ethernet, включать питание и идти настраивать систему.
Первый запуск
И первое, что нужно сделать - разлочить наш rootfs, не зря шифровали же. Сделать это можно как из терминала (например, по UART), так и походом по SSH (спасибо CRYPTROOT_SSH_UNLOCK
):
# ssh -l root -i Armbian-unofficial_24.11.1_Orangepizero3_bookworm_current_.key -p 2222 10.13.0.197
Warning: Permanently added '[10.13.0.197]:2222' (ED25519) to the list of known hosts.
Please unlock disk armbian-root:
Error: Timeout reached while waiting for PID 184.
Connection to 10.13.0.197 closed.
По SSH же можно и продолжить настройку, потому жду какое-то время, пока Armbian запустится (первый запуск всегда долгий - файлуху нужно поресайзить и все такое). Снова иду по SSH (на этот раз настоящий) для последующей настройки:
ssh -l root -p 22 10.13.0.197
, дефолтный пароль:1234
- прохожу мастер, делаю первичную настройку
- заменяю
authorized_keys
дляdropbear-initramfs
на собственный:
piwg:~:# wget -q https://pub.s3.buglloc.cc/authorized_keys -O /etc/dropbear/initramfs/authorized_keys
piwg:~:# update-initramfs -u
update-initramfs: Generating /boot/initrd.img-6.6.60-current-sunxi64
update-initramfs: Armbian: Converting to u-boot format: /boot/uInitrd-6.6.60-current-sunxi64
Image Name: uInitrd
Created: Sun Feb 23 12:07:00 2025
Image Type: AArch64 Linux RAMDisk Image (gzip compressed)
Data Size: 20579752 Bytes = 20097.41 KiB = 19.63 MiB
Load Address: 00000000
Entry Point: 00000000
update-initramfs: Armbian: Symlinking /boot/uInitrd-6.6.60-current-sunxi64 to /boot/uInitrd
'/boot/uInitrd' -> 'uInitrd-6.6.60-current-sunxi64'
update-initramfs: Armbian: done.
- перезагружаюсь, проверяю, что все правки успешно подхватились
Вуаля, имеем минимально настроенную систему с FDE и возможность удаленного анлока.
Network-Bound Disk Encryption
Мне очень нравится NBDE в моей домашней инфре, тем более, что для этого у меня есть целая отдельная железка (см. Homemade FDE: BoundBoxESP). Самое время сходить по SSH и настроить boundbox-initramfs:
- клонирую репо и ставлю нужные скрипты:
piwg:~$ git clone --depth=1 https://github.com/buglloc/boundbox-initramfs.git
piwg:~$ cd boundbox-initramfs/
piwg:~/boundbox-initramfs$ sudo make install
install -Dm755 "etc/initramfs-tools/hooks/boundbox" "/etc/initramfs-tools/hooks/boundbox"
install -Dm755 "etc/initramfs-tools/scripts/local-top/boundbox" "/etc/initramfs-tools/scripts/local-top/boundbox"
install -Dm755 "etc/initramfs-tools/scripts/local-bottom/boundbox" "/etc/initramfs-tools/scripts/local-bottom/boundbox"
install -Dm755 "sbin/boundbox-initramfs" "/usr/local/sbin/boundbox-initramfs"
install -Dm755 "etc/boundbox/initramfs/boundbox.conf" "/etc/boundbox/initramfs/boundbox.conf"
- генерирую новый приватный ключ для SSH и known_hosts для моего инстанса BoundBox:
piwg:~/boundbox-initramfs$ sudo boundbox-initramfs gen
Generate /etc/boundbox/initramfs/key
Generating public/private ed25519 key pair.
Your identification has been saved in /etc/boundbox/initramfs/key
Your public key has been saved in /etc/boundbox/initramfs/key.pub
The key fingerprint is: SHA256:CRl8q2MC11llOyZfM9DaiQYZOUYLKOxLgHixl32kQ6w [email protected]
Check connection to bb.buglloc.cc and generate /etc/boundbox/initramfs/known_hosts
Warning: Permanently added 'bb.buglloc.cc' (ED25519) to the list of known hosts.
Done
- связываю волюм с BoundBox (в конце видно, что добавился новый keyslot и токен
boundbox
):
piwg:~/boundbox-initramfs$ cat /etc/crypttab
# <target name> <source device> <key file> <options>
armbian-root UUID=58d41e13-18ce-4d44-8625-b4bf23ae2fc0 none luks
piwg:~/boundbox-initramfs$ sudo boundbox-initramfs bind --dev UUID=58d41e13-18ce-4d44-8625-b4bf23ae2fc0
Generate salt
Calling boundbox: bb.buglloc.cc
Trying to find free luks slot
Using slot: 1
Check existing token id for slot: 1
Adding luks key to slot '1' from 'UUID=58d41e13-18ce-4d44-8625-b4bf23ae2fc0' with token id ''
Enter any existing passphrase:
Adding luks metadata token id '' for slot '1' from 'UUID=58d41e13-18ce-4d44-8625-b4bf23ae2fc0'
Done
LUKS header information
Version: 2
Epoch: 29
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: 58d41e13-18ce-4d44-8625-b4bf23ae2fc0
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)
Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]
Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2id
Time cost: 6
Memory: 1048576
Threads: 4
Salt: 7b a7 68 23 64 38 c9 60 3c 4b 90 a4 9d cf a2 df
24 93 c2 85 3e 25 e4 48 38 f8 07 39 a6 8e 97 7e
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 1
1: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: pbkdf2
Hash: sha256
Iterations: 1000
Salt: ad 7c 09 55 44 6b db ce 6b 5e c8 24 4b 20 cf 8c
1f 59 62 1e c7 ff 79 32 81 1d 12 ea d4 6b c3 3e
AF stripes: 4000
AF hash: sha256
Area offset:290816 [bytes]
Area length:258048 [bytes]
Digest ID: 1
Tokens:
0: boundbox
Keyslot: 1
Digests:
1: pbkdf2
Hash: sha256
Iterations: 1000
Salt: d1 d2 c7 89 05 64 f9 30 43 65 11 a6 2f 02 6d a2
2c 49 10 8d 61 e5 04 05 75 1b 73 91 89 05 42 78
Digest: 62 71 f0 27 db 95 9c a6 19 53 a7 e9 70 e8 64 b5
c7 f8 e7 88 f7 22 ed 3a 29 dc df aa a9 f7 e8 6c
- обновляю initramfs (подрос на мегабайт с прошлого раза):
piwg:~/boundbox-initramfs$ sudo update-initramfs -u
update-initramfs: Generating /boot/initrd.img-6.6.60-current-sunxi64
update-initramfs: Armbian: Converting to u-boot format: /boot/uInitrd-6.6.60-current-sunxi64
Image Name: uInitrd
Created: Tue Feb 25 20:59:30 2025
Image Type: AArch64 Linux RAMDisk Image (gzip compressed)
Data Size: 21612568 Bytes = 21106.02 KiB = 20.61 MiB
Load Address: 00000000
Entry Point: 00000000
update-initramfs: Armbian: Symlinking /boot/uInitrd-6.6.60-current-sunxi64 to /boot/uInitrd
'/boot/uInitrd' -> 'uInitrd-6.6.60-current-sunxi64'
update-initramfs: Armbian: done.
- перезагружаюсь и проверяю, что теперь моего участия для загрузки не нужно:
[...]
Please unlock disk armbian-root:
IP-Config: no response after 2 secs - giving up
IP-Config: end0 hardware address 02:00:b0:1a:3f:10 mtu 1500 DHCP RARP
IP-Config: end0 complete (dhcp from 10.13.0.1):
address: 10.13.0.197 broadcast: 10.13.255.255 netmask: 255.255.0.0
gateway: 10.13.0.1 dns0 : 10.13.0.1 dns1 : 0.0.0.0
domain : lan
rootserver: 10.13.0.1 rootpath:
filename :
Begin: Starting dropbear ...
try to decrypt /dev/mmcblk0p2
decrypt /dev/mmcblk0p2 with token 0
calling boundbox: SHA256:[email protected]
pass key to passfido
Success: done
Warning: keyslot operation could fail as it requires more than available memory.
done.
done.
Begin: Running /scripts/local-bottom ... Begin: Stopping BoundBox ... done.
done.
Begin: Running /scripts/init-bottom ... Begin: Stopping dropbear ... done.
Begin: Bringing down end0 ... done.
Begin: Bringing down lo ... done.
done.
Welcome to Armbian-unofficial 24.11.1 bookworm!
[...]
Красота, ей богу. Теперь существует целых три способа расшифровать rootfs:
- ручной с консоли
- ручной (но удаленный) по SSH
- автоматический при нахождении BoundBox в сети
Именно так, как я того и хотел ;)
TPM2.0
В целом TPM можно использовать для разных приколюх, например:
- хранить в нем SSH ключи/сертификаты: using a TPM 2.0 to secure ssh keys
- использовать для TLS/mTLS: using mTLS and TPM for digital signage security или mTLS with TPM bound private key
- пошифровать креды при желании: systemd-creds
- использовать в качестве device identity
- да и в том же FDE вполне себе можно использовать (только помните о рисках)
Мне же он нужен для VPN, но это скорее тема для отдельного поста (не обессудьте, текущий и так вышел большим). В этом же хотелось бы его просто завести, для чего:
- прикидываю схему подключения, поглядывая в Orange Pi Zero 3: 26 Pin Interface:
MSI 12Pin SPI TPM Orange Pi Zero3 #1: 3v3 #17: 3v3 #2: SPI CS #24: SPI1_CS #3: MISO #21: SPI1_MISO #4: MOSI #19: SPI1_MOSI #6: SPI CLK #23: SPI1_CLK #7: GND #25: GND #8: SPI RST #26: PC10 - перепаиваю разъем у TPM модуля, т.к. он не подходит по пинам, да и не 2.54mm
- собираю все воедино, поглядывая в распиновку TPM модуля:
- подаю питание, иду по SSH и добавляю нужный device tree overlay:
piwg:~:# mkdir -p /boot/overlay-user
piwg:~:# cat << '_EOF_' > /boot/overlay-user/sun50i-h616-slb9670-spi1_1.dts
/dts-v1/;
/plugin/;
/ {
compatible = "allwinner,sun50i-h616";
fragment@0 {
target-path = "/aliases";
__overlay__ {
spi1 = "/soc/spi@5011000";
};
};
fragment@1 {
target = <&pio>;
__overlay__ {
spi1_cs1_reset: spi1_cs1_reset {
pins = "PC10";
function = "gpio_out";
};
};
};
fragment@2 {
target = <&spi1>;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins &spi1_cs1_pin>;
slb9670: slb9670@1 {
compatible = "infineon,slb9670", "tis,tpm2-spi", "tcg,tpm_tis-spi";
reg = <1>;
gpio-reset = <&spi1_cs1_reset>;
spi-max-frequency = <32000000>;
status = "okay";
};
};
};
};
_EOF_
piwg:~:# armbian-add-overlay /boot/overlay-user/sun50i-h616-slb9670-spi1_1.dts
Compiling the overlay
Copying the compiled overlay file to /boot/overlay-user/
Reboot is required to apply the changes
- перезагружаюсь и о чудо:
piwg:~:# dmesg | grep tpm
[ 26.988879] tpm_tis_spi spi1.1: 2.0 TPM (device-id 0x1B, rev-id 16)
piwg:~:# ls -la /dev/tpm0
crw------- 1 root root 10, 224 Feb 26 22:38 /dev/tpm0
- можно попробовать проинициализировать модуль и сгенерить какой-нить ключ:
piwg:~:# tpm2_ptool init
action: Created
id: 1
piwg:~:# tpm2_ptool addtoken --pid 1 --label sshtok --sopin my-sopin --userpin my-userpin
piwg:~:# tpm2_ptool addkey --algorithm ecc256 --label sshtok --key-label tst --userpin my-userpin
action: add
private:
CKA_ID: '39323561393664343764326364353765'
public:
CKA_ID: '39323561393664343764326364353765'
piwg:~:# pkcs11-tool --module /usr/lib/aarch64-linux-gnu/pkcs11/libtpm2_pkcs11.so --list-token-slots
Available slots:
Slot 0 (0x1): sshtok
token label : sshtok
token manufacturer : Infineon
token model : SLB9670
token flags : login required, rng, token initialized, PIN initialized
hardware version : XXXX
firmware version : XXXX
serial num : 0000000000000000
pin min/max : 0/128
Slot 1 (0x2):
token state: uninitialized
piwg:~:# pkcs11-tool --module /usr/lib/aarch64-linux-gnu/pkcs11/libtpm2_pkcs11.so --slot-index=0 --list-objects
Using slot with index 0 (0x1)
Public Key Object; EC EC_POINT 256 bits
EC_POINT: 044104a4b1dc7f6cb36f95fecb15cba22d359e217e52f6c07274a791bbe1fe69b6192c1cd981f74e6fb05d330e6dfb59c2f84e30942e7415b25425c15758a74035d8da
EC_PARAMS: 06082a8648ce3d030107
label: tst
ID: 39323561393664343764326364353765
Usage: encrypt, verify
Access: local
Пам-пам, можно идти настраивать VPN и все, что нужно.
Conclusion
Надеюсь, мне удалось показать, что не стоит бояться ни FDE, ни TPM даже на SBC. В общем случае все настраивается без особых сюрпризов, разве что с Device Tree overlay придется чуть повозиться (но это не точно). В остальном же, не забывайте, что необходимость любых фичей безопаности (к теме поста: FDE, Secure Boot, TPM, датчики вскрытия, etc) всегда стоит на трех китах:
- модель угроз
- чувство прекрасного
- законы физики
Потому принимайте решение исходя из совокупности этих факторов, а не потому что кто-то в посте так написал. Ну и не забывайте, что серебряной пули (e.g. нельзя воткнуть TPM и больше ни о чем не думать) не существует. Я предупредил, безопасность всегда про комплекс мер ;)
А пока у меня все, не болейте (づ˶•༝•˶)づ♡