Od wielu miesięcy nosiłem się z zamiarem przejścia w końcu na czysto linuxowy system zarządzania zaszyfrowanymi voluminami. Do tej pory korzystałem z konfiguracji LUKS+LVM dla systemowych dysków, a pozostałe partycje były zapieczętowane przy pomocy TC. Ostatnimi dniami doszły mnie słuchy, że TrueCrypt może nie dbać wcale o bezpieczeństwo danych zaszyfrowanych za jego pomocą. Czy wierzyć tym plotkom? Często słyszy się argument, że tak naprawdę nie wiadomo kto stoi za projektem TrueCrypt. Czy mnie to w jakiś sposób porusza? Nie bardzo. Wyobraźmy sobie, że pracujemy nad takim softem umożliwiającym szyfrowanie. Prawdopodobnie zapukają zaraz do nas panowie z NSA w celu dobrowolnej współpracy by zaimplementować w naszym oprogramowaniu jakiegoś backdoora umożliwiającego obejście zabezpieczeń nad którymi pracujemy. A jeśli się nie zgodzimy to prawdopodobnie trafimy do paki za nie podanie hasła do podrzuconego kontenera i pewnie oskarża nas jeszcze o pedofilie, to tak na polskie standardy. No i weź się tu obroń mówiąc, że nie znasz hasła do kontenera i że w ogóle nie wiesz skąd się to wzięło, coś mi to przypomina. xD Także dla mnie, to czemu autorzy softu, który został pobrany prawie 30mln razy, jakoś nie bardzo chcą się ujawniać i przyznawać do czegoś jest w miarę zrozumie ale kto to tam ich wie. Oczywiście można mieć pewne podejrzenia patrząc przez pryzmat braku czegoś co się nazywa "Reproducible Builds" w przypadku binarek, które można pobrać ze strony TC, tak jak to ma miejsce choćby w przypadku TORa. Ale jak wykazała analiza binarek dostępnych na stronie TC, nie ma tam znaczących z punktu widzenia bezpieczeństwa różnic w stosunku do binarek generowanych prosto z kodu źródłowego. Także, można powiedzieć, że powstały one z dostarczonego kodu. Zaś sam kod to już jest odrębna kwestia i by mieć pewność, że nie zawiera on niczego co mogłoby się przyczynić do skompromitowania systemu, trzeba go poddać analizie i w tym celu (biorąc pod uwagę ostatnie doniesienia o NSA) powstała inicjatywa audytu kodu TrueCrypta, której szczegóły można przeczytać tutaj [1]. W każdym razie, po co się bawić TrueCryptem skoro są natywne rozwiązania linuxowe, np. LUKS? No i też, w przeciwieństwie do TC, pakiety od LUKSa są dostępne w debianie Czy używanie LUKSa różni się jakoś znacząco od operowania na kontenerach TC?

TrueCrypt standardowo w obsłudze jest niezbyt wygodny. Co prawda można go dostosować do swoich potrzeb [2], tak by montować wszystko za pomocą jednego klika ale takie rozwiązanie powoduje, że traci się kontrolę nad tym co się montuje. Przykładowo, nie można sprecyzować opcji montowania systemu plików zawartego w otwieranych kontenerach.

W przypadku LUKSa, wszelkie dane potrzebne do poprawnej obsługi systemu plików voluminu są definiowane w /etc/fstab , czyli tam gdzie opcje pozostałych partycji. Mamy także zapewniony support przy sprawdzaniu systemu plików w poszukiwaniu błędów co xx montowań i to zaraz na starcie systemu, tuż po odblokowaniu voluminów -- nie trzeba tego robić ręcznie. Dodatkowo, przy korzystaniu z TrueCrypta pod linuxem, zakładając, że mamy zaszyfrowany system, trzeba wpisywać minimum dwa hasła, nawet w przypadku gdy są one takie same, a przenosząc się z TC na LUKS odpada nam jedno hasło. Poza tym, pozbywamy się także zbędnego pakietu, którego zresztą nie ma w repozytoriach debiana. Użytkownicy windowsa do odczytu kontenerów LUKS mogą użyć FreeOTFE [3]. Niemniej jednak, mając system plików ext4 w kontenerach TC, nigdy nie udało mi się zmusić windowsa do pracy na tych voluminach, już pomijam zapis ext4 pod win, ale nigdy nie udało mi się odczytać danych ext4 zawartych w kontenerze TC pod windowsem. Oczywiście, zaszyfrowany kontener TC z systemem plików ntfs można montować zarówno pod winem jak i linuxem. Nie testowałem wprawdzie nigdy FreeOTFE ale myślę, że raczej nie straci się możliwości odczytu/zapisu voluminów LUKS z systemem plików ntfs.

Wbrew pozorom samo przejście z TC na LUKS nie jest znowu takie trudne jak mogłoby się początkowo wydawać. Jeśli jednak zdecydujemy się pozbyć TC, trzeba dane z partycji zaszyfrowanej gdzieś zgrać, bo stworzenie voluminu LUKS wyczyści ją kompletnie i wszelkie dane na niej zostaną bezpowrotnie utracone. Ja na potrzeby tego artykułu poświęciłem jedną ze swoich głównych partycji, rozmiar 100 GiB, z czego dane wypełniały partycję prawie w połowie -- nie ma to jak operowanie na żywym organizmie.

Gdy już zgramy wszystkie pliki, możemy przystąpić do tworzenia kontenera LUKS:

# cryptsetup --cipher aes-xts-plain64 --key-size 512 --hash sha512 --iter-time 5000 --use-random --verify-passphrase luksFormat /dev/sda8
# cryptsetup luksOpen /dev/sda8 crypt_leon
# mkfs.ext4 -m 0 -L Leon /dev/mapper/crypt_leon 
# mount /dev/mapper/crypt_leon /media/Leon/

Po powyższej operacji jesteśmy w stanie wrzucać już pliki do zaszyfrowanej przestrzeni dysku ale to nie jest koniec naszej pracy. Musimy jeszcze nauczyć system paru sztuczek. Ja u siebie rozważam dwa rozwiązania przy przejściu całkowitym na system LUKS. Pierwszy sposób zakłada, że wszystkie kontenery będą otwierane automatycznie na starcie systemu za pomocą tego samego hasła wpisanego tylko jeden raz. Czyli możemy mieć i 20 różnych partycji, systemowych i niesystemowych, a ich odblokowanie dokona się po wprowadzeniu hasła tylko jeden jedyny raz. Czyli standardowa procedura przy używaniu zaszyfrowanego LVM, z tą różnicą, że w tym przypadku są to oddzielne kontenery. Zaś drugi sposób opiera się na rozdzieleniu systemowych voluminów od tych zewnętrznych i też zakłada możliwość automatycznego montowania wszystkich voluminów przy starcie systemu, tylko w tym przypadku zewnętrzne voluminy będą posiadać inne hasło w stosunku do tych systemowych, czyli trzeba będzie wpisać dwa różne hasła. Przy czym, będzie można zrezygnować z funkcji automatycznego montowania w każdej chwili przez dopisanie jednego parametru do odpowiedniego pliki konfiguracyjnego.

By móc zamontować automatycznie zaszyfrowane voluminy trzeba pierw je skonfigurować przez pliki /etc/fstab oraz /etc/crypttab . Może nie będę opisywał fstaba, bo on jest w miarę zrozumiały, w każdym razie powinien być jeśli się ktoś zabiera za crypttab. Jeśli jednak nie jest, tutaj [4] znajduje się manual.

Dla mnie bardziej ciekawym plikiem jest /etc/crypttab. Tak wygląda mój pliczek:

# <target name>    <source device>       <key file>    <options>
sda2_crypt      UUID=727fa348-8804-4773-ae3d-f3e176d12dac   none        luks
crypt_leon      UUID=dc7f4586-a33d-4707-98e9-8b55c559b0d2   sda2_crypt  luks,keyscript=/lib/cryptsetup/scripts/decrypt_derived

Jak widać mamy w nim dwa wpisy, jeden (górny) jest od partycji systemowych (LVM), drugi zaś od co dopiero zaszyfrowanego voluminum. Składnia pliku jest prosta: najpierw podawana jest nazwa pod jaką zostanie otwarty kontener (crypt_leon), następnie jest definiowana partycja po UUID, trzecie pole definiuje w jaki sposób kontener zostanie otwarty, może to być hasło albo keyfile, ostatnie pole to opcje. W przypadku pierwszego wpisu -- none, oznacza, że trzeba będzie podać hasło przy starcie systemu, natomiast w drugim wpisie, sda2_crypt wskazuje volumin, którego hasło (choć to nie dokońca jest to samo hasło) zostanie użyte do otworzenia tego kontenera. Czyli by otworzyć crypt_leon potrzebne jest wpisanie hasła do sda2_crypt. Po jego wpisaniu, crypt_leon zostanie automatycznie odblokowany. Ale tego nie da się tak prosto osiągnąć. Trzeba skorzystać ze skryptu /lib/cryptsetup/scripts/decrypt_derived .

Jeszcze parę słów o opcjach jakie można dopisać w powyższym pliku. Nie będę wypisywał wszystkich tutaj, bo te można znaleźć na http://www.freedesktop.org/software/systemd/man/crypttab.html [5]. Dwie najważniejsze to: noauto oraz readonly. Dopisanie pierwszej z nich sprawi, że volumin z taką opcją nie zostanie zamontowany na starcie systemu, podczas gdy drugi przełącza system plików w tryb tylko do odczytu.

Trzeba także odróżnić od siebie dwa UUID. Tak wygląda specyfikacja mojego dysku:

root:~# lsblk -o name,size,type,mountpoint,uuid
NAME                             SIZE TYPE  MOUNTPOINT    UUID
sda                              1.4T disk                
├─sda1                           953M part  /boot         f557b9f0-edb5-42bb-94d8-27bc03c3c2c7
├─sda2                          46.6G part                727fa348-8804-4773-ae3d-f3e176d12dac
│ └─sda2_crypt (dm-0)           46.6G crypt               P1kvJI-5iqv-s9gJ-8V2H-2EEO-q4aK-sx4aDi
│   ├─debian_crypt-swap (dm-1)     2G lvm   [SWAP]        af41e491-730f-4d25-b194-5cd3d1d08c94
│   ├─debian_crypt-tmp (dm-2)      5G lvm   /tmp          bb06edd9-6668-4542-9fe5-e8cea49dabc7
│   ├─debian_crypt-home (dm-3)     6G lvm   /home         12e8566c-8f0f-45ec-8524-6d9d9ee91eae
│   └─debian_crypt-root (dm-4)  33.6G lvm   /             02670801-740e-4799-b99d-ae1cc9d12f4b
├─sda3                         651.9G part                
│ └─truecrypt3 (dm-8)          651.9G dm    /media/Filmy  9d80d97e-a8f5-4a84-9f0e-5530cdfb4e56
├─sda4                             1K part                
├─sda5                           298G part                5063da5f-9b68-43de-914c-32b89622bcc8
│ └─crypt_p2p (dm-7)             298G crypt /media/p2p    e6a0b66c-8fe9-4e7b-9d54-7b2b430e109d
├─sda6                         213.6G part                5129d860-bb41-4393-b4b1-f8af53d9155d
│ └─crypt_dane (dm-6)          213.6G crypt /media/Dane   19101155-6070-4f37-b39d-19f28867c66b
├─sda7                          85.6G part  /media/Server a9f4dae5-901c-4f49-bb30-592de3000713
└─sda8                         100.6G part                dc7f4586-a33d-4707-98e9-8b55c559b0d2
  └─crypt_leon (dm-5)          100.6G crypt /media/Leon   5e3242e1-ec7a-4806-92f7-88a126feea94

Jak można zauważyć sda8 oraz crypt_leon mają dwa różne UUID. Którego zatem mamy użyć w przypadku pisania regułek w plikach /etc/fstab oraz /etc/crypttab ? Ten od crypt_leon -- 5e3242e1-ec7a-4806-92f7-88a126feea94 -- trzeba wpisać do /etc/fstab , natomiast w /etc/crypttab trzeba podać UUID partycji sda8, w tym przypadku jest to dc7f4586-a33d-4707-98e9-8b55c559b0d2 .

By móc użyć /lib/cryptsetup/scripts/decrypt_derived do automatycznego montowania, trzeba pierw wygenerować keyfile-hasło, którego to zawartość zostanie na stałe dodana do nagłówka kontenera, który ma być otwierany na starcie systemu. Można to zrobić ręcznie albo też posłużyć się w tym celu powyższym skryptem. Poniżej wykorzystanie skryptu:

# /lib/cryptsetup/scripts/decrypt_derived sda2_crypt > klucz

Plik będzie zawierał reprezentację hexadecymalną klucza binarnego użytego do szyfrowania/deszyfrowania danych na wskazanej partycji. Ten parametr, jak i szereg innych, można wyciągnąć z:

# dmsetup table --showkeys

Ciąg składa się ze 128 znaków i można go ręcznie wpisać do drugiego slota w nagłówku zaszyfrowanej partycji albo też można w tym celu wskazać wygenerowany plik. W każdym razie oba rozwiązania mają dokładnie taki sam cel -- utworzenie nowego keylota ze 128 znakowym hasłem. Ten drugi keyslot jest tworzony na wzór keyfile ale jak to z keyfile bywa, zwykle się go trzyma np. na pendirve i ktoś może go podejrzeć lub zgrać. A tutaj ładuje się do pamięci jeden klucz szyfrujący który jest trzymany w hexalnej postaci przez dmsetup, ten ciąg zawsze będzie inny dla każdej zaszyfrowanej partycji, dlatego nadaje się na keyfile, do którego dostęp można uzyskać tylko po podaniu odpowiedniego hasła. Bez hasła nie będzie klucza, nie będzie ciągu i nie będzie można go wydobyć z dmsetup. Przy czym w nagłówku partycji sda8 mamy zajęte dwa sloty, co uniezależnia nas od nagłówka partycji sda2, czyli możemy montować sobie dysk na innych maszynach bez większego problemu. Można oczywiście skasować pierwszy slot z rzeczywistym hasłem ale wtedy jeśli coś się stanie z nagłówkiem partycji systemowej, dane na sda8 zostaną bezpowrotnie utracone. Moim zdaniem lepiej jest dodać drugi slot do nagłówka:

# cryptsetup luksAddKey /dev/sda8 klucz

Sprawdzamy czy mamy zajęte dwa sloty:

# cryptsetup luksDump /dev/sda8

Jeśli tak, znaczy, że wszystko jest w porządku. Kontener powinien się odblokować na starcie systemu, przynajmniej mój tak robi. xD Nie musimy oczywiście restartować maszyny by sprawdzić czy powyższe kroki zostały przeprowadzone prawidłowo. Możemy to zrobić przy pomocy:

# /lib/cryptsetup/scripts/decrypt_derived sda2_crypt | cryptsetup luksOpen /dev/sda8 crypt_leon

Zakładając, że pliki /etc/crypttab oraz /etc/fstab mają odpowiednie linijki oraz, że w wyniku powyższego polecania kontener został otworzony, można być pewnym, że zostanie otworzony również przy starcie systemu.

Dobrze jest też zrobić sobie backup nagłówków zaszyfrowanych partycji oraz backup mbr [6] i trzymać te pliki w bezpiecznym miejscu:

# cryptsetup luksHeaderBackup /dev/sda8 --header-backup-file sda8_header_backup
# ls -al sda8_header_backup 
-r-------- 1 root root 2.0M Nov  7 12:36 sda8_header_backup

Przy czym taka ważna uwaga -- lepiej jest nie tworzyć kopi nagłówków lub też i samych kluczy bezpośrednio na dysku, zostają po nich ślady, oczywiście można użyć shreda na plikach gdy już skończymy się bawić z ustawieniami ale moim zdaniem lepiej jest przeprowadzać takie operacje bezpośrednio w pamięci RAM.

Ja mam u siebie mały ramdisk, który jest odpowiednio skonfigurowany przy tym w /etc/fstab:

tmpfs     /tmp_ram    tmpfs      noauto,nodev,nosuid,size=20m,mode=1700      0       0

Jak widać, ma on 20MiB i trzeba go ręcznie zamontować w /tmp_ram/ , to tam przeprowadzałem operacje min. dodawania klucza do nagłówka. Po odmontowaniu, ślad po plikach znajdujących się w tym katalogu znika, a po chwili obszar RAM przeznaczony pod ten katalog zostanie nadpisany przez dane, które zostaną wczytane do niego w wyniku użytkowania PC, no i oczywiście nie zostawiliśmy śladu w żadnych strukturach dysku. W przypadku kopii nagłówków, można je bezpośrednio z RAMu kopiować na inne urządzenie służące za backup. W ten sposób będziemy mieć pewność, że kopia nagłówka znajduje się tylko w jednym miejscu. Więcej informacji na temat tego jak obchodzić się z nagłówkami zaszyfrowanych partycji można znaleźć tutaj [7].

Cała ta zabawa z tym skryptem decrypt_derived przypomina mi trochę odszyfrowanie dysku przy pomocy binarnego keyfile, który jest fizycznie zlokalizowany na innym zaszyfrowanym dysku, w tym wypadku dysku systemowym, np w katalogu /root/ . Wtedy po odszyfrowaniu systemu uzyskuje się dostęp do keyfile i można zamontować kontener przy jego pomocy. Można sprecyzować ścieżkę do binarnego keyfile w /etc/crypttab i na dobrą sprawę nie ma większej różnicy między jednym i drugim rozwiązaniem. No chyba, że się nada złe prawa do pliku, wtedy będzie można odczytać keyfile na działającej maszynie, zaś dostęp do tabeli z kluczami w dmsetup wymaga uprawnień roota. Więcej na temat postaci jakie może przyjąć keyfile można przeczytać tutaj [8].

Update:

Okazuje się, że istnieje prostsza metoda na automatyczne montowanie pożądanych voluminów na starcie systemu przy pomocy tego samego hasła. Wykorzystuje ona nieco inny skrypt -- /lib/cryptsetup/scripts/decrypt_keyctl i w ten sposób odpada nam cała zabawa z edycją nagłówków poszczególnych kontenerów. Teoretyczną wadą tego rozwiązania jest trzymanie hasła w pamięci, a konkretnie w keyringu kernela. Oczywiście niepermanentnie, tylko przez 60s, akurat na czas startu systemu. Szkoda tylko, że nie można dostosować tego do własnych potrzeb. W moim przypadku wystarczyło by 15 max 20s, a zjechanie z 60s do 20s by zwiększyło bezpieczeństwo dość znacząco.

By skorzystać z tego skryptu trzeba mieć zainstalowan w systemie pakiet keyutils. Trzeba także dostosować odpowiednio /etc/crypttab . Poniżej wycinek mojego pliku:

sda2_crypt       UUID=727fa348-8804-4773-ae3d-f3e176d12dac   haslo       luks,keyscript=decrypt_keyctl
crypt_leon      UUID=dc7f4586-a33d-4707-98e9-8b55c559b0d2   haslo       luks,keyscript=decrypt_keyctl

Zmieniło się trzecie pole, które zwykło odpowiadać za keyfile oraz oczywiście zmienił się keyscript. Za co dokładnie teraz odpowiada trzecie pole? Jest to identyfikator i można tam wpisać dowolny ciąg znaków, grunt w tym, żeby był on taki sam dla wszystkich voluminów, których otwarcie wymaga podania tego samego hasła.

Ostatnim krokiem jest przebudowanie initramfs:

# update-initramfs -u -k all

W przypadku gdyby obraz nie chciał się wygenerować i np. zwróci poniższy błąd:

# update-initramfs: Generating /boot/initrd.img-3.12-0.slh.3-aptosid-amd64
E: /usr/share/initramfs-tools/hooks/cryptkeyctl failed with return 1.
update-initramfs: failed for /boot/initrd.img-3.12-0.slh.3-aptosid-amd64 with 1.

oznacza to prawdopodobnie brak zainstalowanego pakietu keyutils.


Przypisy:

  1. https://wiki.debian.org/ReproducibleBuilds
  2. http://dug.net.pl/tekst/239/jak_dbac_o_swoja_prywatnosc___nie_tylko_w_sieci/#10
  3. http://en.wikipedia.org/wiki/FreeOTFE
  4. http://man.przez.net/fstab.5.html
  5. http://www.freedesktop.org/software/systemd/man/crypttab.html
  6. http://dug.net.pl/tekst/247/zaszyfrowany_debian_from_scratch_przy_wykorzystaniu_debootstrap/#2.8.25.1.
  7. https://wiki.archlinux.org/index.php/dm-crypt_with_LUKS#Backup_the_cryptheader
  8. https://wiki.archlinux.org/index.php/dm-crypt_with_LUKS#Using_Cryptsetup_with_a_Keyfile