Nagłówek kontenera LUKS trzymany na pendrive

Spis treści

Jeśli kiedyś rozważaliśmy umieszczenie pliku klucza (keyfile) do zaszyfrowanego kontenera LUKS na pendrive, to ciekawszą alternatywą może okazać się umieszczenie całego nagłówka takiego kontenera na zewnętrznym nośniku. Ma to tę przewagę nad keyfile, że wszystkie informacje zapewniające dostęp do kontenera, wliczając w to klucz główny, są oddzielone od zaszyfrowanych danych. W ten sposób nawet jeśli kontener wpadnie w niepowołane ręce, to nie ma żadnego sposobu na to, by ten ktoś te dane odzyskał, no bo przecie nie ma klucza szyfrującego. Przechwycenie hasła również nic to nie zmieni, no chyba, że ten ktoś zdobędzie również pendrive z nagłówkiem kontenera. Z ludzkiego punktu widzenia, to na takim dysku będą znajdować się jedynie losowe dane i do tego w formie kompletnie nieczytelnej dla człowieka (brak systemu plików). Niemniej jednak, jest kilka rzeczy, o których warto pamiętać, gdy w grę wchodzi nagłówek LUKS i to o nich porozmawiamy sobie w tym wpisie.

Nagłówek LUKS

Kontenery LUKS mają nagłówki. Zwykle jest to pierwsze 2 MiB danych, zaraz na początku zaszyfrowanej partycji. Sam kontener i jego nagłówek są niezależne od siebie. Mogą zatem istnieć na innych fizycznych mediach, np. kontener na jednym dysku, a jego nagłówek na pendrive. Jeśli taki schemat zastosujemy, to do otworzenia kontenera będzie nam potrzebny ten zewnętrzny nośnik, o czym trzeba pamiętać podczas startu systemu, oczywiście jeśli będziemy chcieli go w tym czasie montować ale tym zajmiemy się później. Natomiast teraz stwórzmy sobie kontener, przykładowo:

# cryptsetup \
  --cipher aes-xts-plain64 \
  --key-size 512 \
  --hash sha512 \
  --iter-time 5000 \
  --use-random \
  --verify-passphrase \
  --verbose \
  luksFormat /dev/sda2

# cryptsetup luksOpen /dev/sda2 sda2

# mkfs.ext4 /dev/mapper/sda2

# cryptsetup luksClose sda2

Na dobrą sprawę, to pierwsze polecenie, zapisało jedynie dwa pierwsze megabajty na partycji /dev/sda2 i to właśnie jest nasz nagłówek, który musimy wydobyć i zapisać w osobnym pliku. Kopię nagłówka możemy zrobić przy pomocy dd albo też użyć do tego celu luksHeaderBackup . To drugie rozwiązanie nas uchroni przed popełnieniem ewentualnych błędów i dlatego to z niego skorzystamy:

# cryptsetup luksHeaderBackup --header-backup-file header.img /dev/sda2

Usuwanie nagłówka LUKS

Mając nagłówek w pliku, możemy teraz nadpisać odpowiedni obszar partycji. Jest tylko jeden problem. Jeśli byśmy nadpisali cały nagłówek przy pomocy dd , to system nie rozpozna tej partycji jako kontener LUKS. Jeśli jej nie rozpozna, to nie wykryje jej UUID, a bez UUID będą problemy z automatycznym montowaniem takiego zasobu.

Zgodnie z informacjami jakie znalazłem w FAQ cryptsetup, nagłówek LUKS ma pewien określony format:

Refers to LUKS On-Disk Format Specification Version 1.2.1

LUKS header:

offset  length  name             data type  description
-----------------------------------------------------------------------
0x0000   0x06   magic            byte[]     'L','U','K','S', 0xba, 0xbe
     0      6
0x0006   0x02   version          uint16_t   LUKS version
     6      3
0x0008   0x20   cipher-name      char[]     cipher name spec.
     8     32
0x0028   0x20   cipher-mode      char[]     cipher mode spec.
    40     32
0x0048   0x20   hash-spec        char[]     hash spec.
    72     32
0x0068   0x04   payload-offset   uint32_t   bulk data offset in sectors
   104      4                               (512 bytes per sector)
0x006c   0x04   key-bytes        uint32_t   number of bytes in key
   108      4
0x0070   0x14   mk-digest        byte[]     master key checksum
   112     20                               calculated with PBKDF2
0x0084   0x20   mk-digest-salt   byte[]     salt for PBKDF2 when
   132     32                               calculating mk-digest
0x00a4   0x04   mk-digest-iter   uint32_t   iteration count for PBKDF2
   164      4                               when calculating mk-digest
0x00a8   0x28   uuid             char[]     partition UUID
   168     40
0x00d0   0x30   key-slot-0       key slot   key slot 0
   208     48
0x0100   0x30   key-slot-1       key slot   key slot 1
   256     48
0x0130   0x30   key-slot-2       key slot   key slot 2
   304     48
0x0160   0x30   key-slot-3       key slot   key slot 3
   352     48
0x0190   0x30   key-slot-4       key slot   key slot 4
   400     48
0x01c0   0x30   key-slot-5       key slot   key slot 5
   448     48
0x01f0   0x30   key-slot-6       key slot   key slot 6
   496     48
0x0220   0x30   key-slot-7       key slot   key slot 7
   544     48

Będąc w posiadaniu tych informacji, musimy zamazać deskryptory haseł (widoczne powyżej jako keyslot) oraz bajty od 6-168. Chodzi o to, że w pierwszych 6 bajtach nagłówka jest zawarta informacja, że jest to kontener LUKS. Linux potrzebuje tej informacji, by skutecznie wykryć typ partycji. Dlatego też nie możemy jej zamazać. Na pozycji 168 jest ulokowany UUID tego kontenera. Wszystkie narzędzia, które mają operować na tym kontenerze, potrzebują tego numerka. Dlatego te dwa pola muszą zostać nietknięte, a całą resztę możemy zamazać. Robimy to w poniższy sposób:

# cryptsetup luksKillSlot /dev/sdb2 0

# dd if=/dev/zero of=/dev/sdb2 bs=1 seek=6 count=162

Jeśli teraz spróbujemy odczytać informację z nagłówka takiego kontenera przy pomocy luksDump , to powinniśmy otrzymać mniej więcej taki komunikat:

# cryptsetup luksDump /dev/sdb2
Unsupported LUKS version 0

Mimo, że system nie jest w stanie odczytać nagłówka tego kontenera, to wciąż jest w stanie ustalić jego UUID:

# partprobe

# lsblk -f /dev/sdb
NAME         FSTYPE      LABEL UUID                                   MOUNTPOINT
sdb
├─sdb1       LVM2_member       VsO7QD-1uxK-q0zr-KDa4-DVSM-Levr-p93ewp
│ ├─lvm-root ext4        root  ee759aa3-4977-4c08-b14f-a62d17a47478   /
│ └─lvm-swap swap              bf480ef5-e5f0-43b9-9282-c3f1d0d446fa
└─sdb2       crypto_LUKS       11f480d6-950b-40ff-9739-e8244a4b5a30

I o takie coś nam chodzi.

Umieszczanie nagłówka LUKS na pendrive

Przy zaszyfrowanych dyskach i pewnych niestandardowych ustawieniach systemu, katalog /boot/ zwykle jest ulokowany na osobnej partycji. Możemy ten mechanizm wykorzystać pozbywając się jednocześnie problemów z niezabezpieczonym initramfs, czyli modułami jądra i innymi plikami, które mają na celu zamontowanie głównego systemu plików. Jakby nie patrzeć, wszystkie pliki na partycji /boot/ pozostają w formie odszyfrowanej, co czyni je podatnymi na zmianę bez jakiejkolwiek kontroli i weryfikacji z naszej strony. Obejdziemy tym też problemy związane z kodem bootloader'a, który znajduje się w MBR. Odpadną nam przy tym też wszelkie ataki wymierzone w ten sektor. Jedyna niedogodność jest taka, że ten pendrive będzie wymagany do uruchomienia naszego systemu. Choć to ma też swoje zalety.

Wsadzamy zatem pendrive to portu USB i montujemy jego partycję w systemie. Następnie kopiujemy całą zawartość katalogu /boot/ i plik nagłówka LUKS na partycję pendrive:

# mount /dev/sdc1 /mnt/
# cp -a /boot/* /mnt/
# cp -a header.img /mnt/
# sync

W ten sposób możemy odmontować partycję /boot/ i zamontować w jej miejsce partycję pendrive:

# umount /mnt/
# umount /boot/
# mount /dev/sdc1 /boot/

Edytujemy teraz plik /etc/fstab i dostosowujemy odpowiednie wpisy, przykładowo:

UUID=ee759aa3-4977-4c08-b14f-a62d17a47478 /                 ext4 defaults 0 1
UUID=1bbe97b4-9116-4366-843c-187ba5f0323f /boot             ext4 defaults 0 2
UUID=355933fa-7c8a-46df-93d8-3d3395327f29 /media/storage    ext4 defaults 0 2

Musimy także odpowiednio dostosować sobie plik /etc/crypttab :

storage   UUID=11f480d6-950b-40ff-9739-e8244a4b5a30   none    luks,header=/boot/header.img

Dlatego właśnie potrzebowaliśmy UUID kontenera. Z kolei w parametrze header podajemy lokalizację nagłówka.

Musimy także przereinstalować bootloader. W tym przypadku jest to grub2 :

# dpkg-reconfigure grub-pc
Installing for i386-pc platform.
Installation finished. No error reported.
Generating grub configuration file ...
Found background image: /usr/share/images/desktop-base/desktop-grub.png
Found linux image: /boot/vmlinuz-4.3.0-1-amd64
Found initrd image: /boot/initrd.img-4.3.0-1-amd64
done

W momencie, gdy zostaniemy poproszeni o podanie dysku, na którym ma zostać umieszczony kod bootloader'a, wybieramy pendrive:

grub-instalacja-pendrive-luks-naglowek

Następnie regenerujemy initramfs:

# update-initramfs -u -k all
update-initramfs: Generating /boot/initrd.img-4.3.0-1-amd64

Usługa dla systemd

Wszystkie zaszyfrowane kontenery LUKS w systemd są otwierane przed zamontowaniem partycji /boot/ . W takim przypadku ten kontener, którego nagłówek obcięliśmy, nie odpali nam się. Musimy zatem nieco przerobić domyślną usługę dla systemd. Tworzymy plik systemd-cryptsetup@storage.service w katalogu /etc/systemd/system/ i wrzucamy do niego tę poniższą treść:

[Unit]
Description=Cryptography Setup for %I
Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)
DefaultDependencies=no
Conflicts=umount.target
BindsTo=dev-mapper-%i.device
IgnoreOnIsolate=true
After=cryptsetup-pre.target
After=boot.mount
BindsTo=dev-disk-by\x2duuid-11f480d6\x2d950b\x2d40ff\x2d9739\x2de8244a4b5a30.device
After=dev-disk-by\x2duuid-11f480d6\x2d950b\x2d40ff\x2d9739\x2de8244a4b5a30.device
Before=umount.target

[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutSec=0
ExecStart=/lib/systemd/systemd-cryptsetup attach 'storage' '/dev/disk/by-uuid/11f480d6-950b-40ff-9739-e8244a4b5a30' 'none' 'luks,header=/boot/header.img'
ExecStop=/lib/systemd/systemd-cryptsetup detach 'storage'

Plik tej powyższej usługi został jedynie przekopiowany z katalogu /run/systemd/generator/ i dostosowany pod kątem zależności. Nie musimy go pisać od początku.

Przeładowujemy systemd i dodajemy tę usługę do autostartu:

# systemctl daemon-reload
# systemctl enable systemd-cryptsetup@storage.service

By sprawdzić czy systemd jest w stanie odszyfrować i zamontować ten zasób, możemy zwyczajnie wystartować tę usługę lub też uruchomić komputer ponownie. Niemniej jednak, podczas startu systemu, zostaniemy poproszeni o hasło. Jeśli nie chce nam się dodatkowo wpisywać hasła podczas startu, to zawsze możemy korzystać z keyfile, który również można umieścić na pendrive. Choć nie zalecałbym tego rozwiązania. Można także skorzystać z keyring'u kernela, który jest w stanie przechowywać hasła LUKS, o ile ma się zaszyfrowaną jakąś partycję, najlepiej tę systemową. W ten sposób unikniemy wprowadzania wielu haseł do zaszyfrowanych zasobów. Po odszyfrowaniu systemu, partycję /boot/ można zwyczajnie odmontować, a pendrive wyciągnąć. Pamiętajmy jednak, by nie aktualizować bootloader'a czy kernela, gdy partycja /boot/ jest niezamontowana.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.