Jakiś czas temu, w tym artykule [1] opisywałem jak przy pomocy truecrypta w prosty sposób uczynić pliki na dropboxie bardziej prywatnymi. Poniższy tekst opisuje podobny schemat, z tym, że w oparciu o natywne narzędzie debianowe -- LUKS, co niesie ze sobą sporo udogodnień i czyni korzystanie z zaszyfrowanego dropboxa praktycznie transparentne.

Przede wszystkim musimy utworzyć plik składający się z samych zer. Tak stworzony plik umieszczamy na dropboxie. Sam plik może być dowolnego rozmiaru -- 100m , 10g -- nie ma znaczenia, liczy się tylko ile miejsca chcemy przeznaczyć na dropboxie na szyfrowane rzeczy. Najciekawsze w tym wszystkim jest to, że plik składający się z samych zer zostanie przed wysłaniem do chmury dropboxa skompresowany, a kompresja samych zer daje wynik porównywalny z zerem. I tak np. 10GiB plik będzie zajmować parę KiB, co oczywiście cieszy bo nie będzie trzeba wysyłać do chmury całych 10GiB danych.

Stopujemy lub pauzujemy synchronizację dropboxa i przechodzimy do katalogu gdzie mamy swoją lokalną kopię plików i tworzymy pliczek -- przykładowo 1GiB:

$ cd /media/Server/Dropbox/
$ dropbox status
Syncing paused
$ dd if=/dev/zero of=./luks_dropbox bs=2M count=500
500+0 records in
500+0 records out
1048576000 bytes (1.0 GB) copied, 11.9748 s, 87.6 MB/s
$ ls -al | grep -i luks
-rw-r--r--  1 morfik morfik 1000M Nov 23 15:25 luks_dropbox

Puszczamy synchronizację:

$ dropbox filestatus
luks_dropbox:        syncing

I po chwili dostajemy komunikat:

$ dropbox filestatus

luks_dropbox:        up to date

Nie znalazlem opcji wyłączenia sync pod konsolą ale można to zrobić przez ikonkę w trayu. Jeśli ktoś preferuje konsolę, to chyba nie ma innego wyjścia jak tylko zastopować dropboxa przy pomocy dropbox stop . Zatrzymujemy sync ponownie, logujemy się na roota i przygotowujemy urządzenie loop, które obsłuży kontener dropboxa:

$ su
Password: 
# losetup /dev/loop0 /media/Server/Dropbox/luks_dropbox 
# losetup -a
/dev/loop0: [0807]:2883594 (/media/Server/Dropbox/luks_dropbox)

Teraz trzeba zaszyfrować plik:

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

WARNING!
========
This will overwrite data on /dev/loop0 irrevocably.

Are you sure? (Type uppercase yes): YES
Enter passphrase: 
Verify passphrase: 
Command successful.

Otwieramy kontener i tworzymy na nim system plików:

# cryptsetup luksOpen /dev/loop0 crypt_dropbox
Enter passphrase for /media/Server/Dropbox/luks_dropbox: 
# ls -al /dev/mapper/ | grep -i dropbox
lrwxrwxrwx  1 root root       7 Nov 23 15:54 crypt_dropbox -> ../dm-9
# mkfs.ext4 -m 0 -L dropbox /dev/mapper/crypt_dropbox
mke2fs 1.42.8 (20-Jun-2013)
Filesystem label=dropbox
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
63872 inodes, 255488 blocks
0 blocks (0.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=264241152
8 block groups
32768 blocks per group, 32768 fragments per group
7984 inodes per group
Superblock backups stored on blocks: 
    32768, 98304, 163840, 229376

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

W tej chwili mamy przygotowany kontener na dane, zamykamy go i synchronizujemy dropboxa:

# cryptsetup luksClose crypt_dropbox 
$ dropbox status
Updating (1 file)
Indexing "luks_dropbox"...
$ dropbox status
Updating (1 file, 9 mins left)
Uploading "luks_dropbox" (70.0 kB/sec, 9 mins left)
$ dropbox status
Idle

Nie wiem czemu wyrzuca prawie 10 min. U mnie trwało to o połowę krócej. W każdym razie, teraz musimy nauczyć system jak automatycznie montować ten kontener. Ja już mam w swoim systemie kilka zaszyfrowanych dysków, dlatego też u mnie nie będzie extra hasła, które trzeba by podawać przy starcie systemu. Hasła co prawda są inne, i w przypadku systemu, i w przypadku kontenera dropbox ale przy pomocy ciekawej sztuczki można uzależnić kontener dropboxa od systemowego klucza. Można też wykasować hasło do kontenera dropbox ale wtedy uzależnimy kontener zupełnie od nagłówków tej partycji. Jeśli te nagłówki ulegną uszkodzeniu albo przez przypadek stworzymy nowy kontener pod nowy system, stracimy dostęp do danych na dropboxie. Tyle teorii, bierzemy się za automontowanie kontenera.

Przede wszystkim losetup musi widzieć nasz kontener:

#  losetup -a
/dev/loop0: [0807]:2883594 (/media/Server/Dropbox/luks_dropbox)

Edytujemy teraz dwa pliki: /etc/crypttab oraz /etc/fstab . Konfiguracja /etc/crypttab będzie inna w zależności od tego czy mamy już zaszyfrowane dyski czy tez nie. Jeśli nie mamy to trzeba będzie niestety podawać na starcie systemu hasło. Można też sprecyzować keyfile, choć na niezaszyfrowanym systemie nie polecam tego rozwiązania. Keyfile jest dopuszczalną formą gdy rezyduje na zaszyfrowanej przestrzeni, w chronionej przez roota lokalizacji (prawa 400). Także jeśli nie mamy zaszyfrowanego systemu, musimy ograniczyć się tylko do hasła. Poniżej przykładowy wpis w /etc/crypttab :

crypt_dropbox         /media/Server/Dropbox/luks_dropbox   none      luks,noauto

Ja nie będę się zajmował powyższym rozwiązaniem, jest ono wielce niepraktyczne, dlatego przecie wykorzystuje full disk encryption i jak zobaczymy poniżej, to powoli zaczyna ułatwiać życie.

Tak powinien mniej więcej wyglądać plik /etc/crypttab przy posiadaniu zaszyfrowanego dysku systemowego:

sda2_crypt       UUID=727fa348-8804-4773-ae3d-f3e176d12dac   none        luks
crypt_dropbox   /media/Server/Dropbox/luks_dropbox          sda2_crypt  luks,keyscript=/lib/cryptsetup/scripts/decrypt_derived,noauto

Teraz jeszcze trzeba wyciągnąć 128 znakowy ciąg z tabeli dmsetup przy pomocy dmsetup table --showkeys i dodać go do nagłówka kontenera dropbox jako hasło. Właściwy ciąg, który nas interesuje w tej tabeli jest definiowany przez sda2_crypt w pliku /etc/crypttab:

# cryptsetup luksAddKey /media/Server/Dropbox/luks_dropbox 
Enter any existing passphrase: 
Enter new passphrase for key slot:

Sprawdzamy czy są zajęte dwa sloty:

# cryptsetup luksDump /media/Server/Dropbox/luks_dropbox 
LUKS header information for /media/Server/Dropbox/luks_dropbox

Version:        1
Cipher name:    aes
Cipher mode:    xts-plain64
Hash spec:      sha512
Payload offset: 4096
MK bits:        512
MK digest:      84 1e 7a 83 e4 c8 cb c3 d9 ce 0c 24 d1 91 a8 4f ea 55 48 ea 
MK salt:        e2 f5 3c 2d 9f 0b 6e fe 05 39 2c f1 83 f3 d5 6d 
                6a a0 d3 01 e0 f4 07 88 c2 22 1b 53 16 8b 7f 1b 
MK iterations:  42500
UUID:           a3be80bf-0f2c-44ed-8de5-d60e3b19c01a

Key Slot 0: ENABLED
    Iterations:             166835
    Salt:                   00 a8 d7 38 ce 29 24 76 67 6e 60 77 d3 55 ec 5f 
                            47 35 17 3b 23 19 1b ff 92 0d ea 03 3f e6 b9 78 
    Key material offset:    8
    AF stripes:             4000
Key Slot 1: ENABLED
    Iterations:             34668
    Salt:                   77 14 9f 80 c1 a8 12 fb 21 6f 3b 5e eb 95 98 ac 
                            99 ca b9 21 a1 da 59 d6 62 c6 53 e8 8f 57 7e a0 
    Key material offset:    512
    AF stripes:             4000
Key Slot 2: DISABLED
Key Slot 3: DISABLED
Key Slot 4: DISABLED
Key Slot 5: DISABLED
Key Slot 6: DISABLED
Key Slot 7: DISABLED

W tej chwili system będzie w stanie wykorzystać hexalną wartość klucza partycji systemowej do odblokowania kontenera dropboxa. Możemy sprawdzić czy tak faktycznie się stanie. By otworzyć kontener wydajemy poniższe polecenie:

# cryptdisks_start crypt_dropbox 
[....] Starting crypto disk...[info] crypt_dropbox (starting)...
Unlocking the disk /media/Server/Dropbox/luks_dropbox (crypt_dropbox)
[ ok  passphrase: crypt_dropbox (started)...done.

By zamknąć:

# cryptdisks_stop crypt_dropbox 
[ ok ] Stopping crypto disk...crypt_dropbox (stopping)...done.

cryptdisk działa w oparciu o plik /etc/crypttab i by z niego skorzystać, trzeba mieć w tym pliku zdefiniowane kontenery. W każdym razie otwórzmy kontener dropboxa, bo jakby nie patrzeć to nie jest jeszcze koniec naszej pracy. Musimy min. dodać odpowiedni wpis w /etc/fstab . By to zrobić trzeba uzyskać UUID systemu plików w kontenerze dropbox:

# tune2fs -l /dev/mapper/crypt_dropbox | grep UUID
Filesystem UUID:          0d959e74-ec19-43bf-b779-60134c676aef

Edytujemy plik /etc/fstab i dodajemy tam poniższy wpis:

# dropbox
UUID=0d959e74-ec19-43bf-b779-60134c676aef   /media/Dropbox  ext4    defaults,noauto,user,nofail,noatime,commit=20   0 2

Tworzymy także punkt montowania w /media/ :

# mkdir /media/Dropbox

By zadbać o poufność danych w tym kontenerze na systemie, do którego ma dostęp wielu użytkowników, musimy jeszcze przeprowadzić kilka czynności. Montujemy zatem system plików kontenera dropboxa:

# mount /media/Dropbox
# chown -R morfik:morfik /media/Dropbox/
# chmod 700 -R /media/Dropbox/

I do tego ustawiamy jeszcze odpowiedni ACL:

# setfacl -n -d -m u::rwx,g::---,o::--- /media/Dropbox

Tak skonfigurowany kontener będzie produkował pliki i foldery z uprawnieniami odpowiednio 600 i 700 .

Przy otwieraniu kontenera via /etc/crypttab jest pewien problem, bo ten kontener znajduje się na partycji, a jej system plików z kolei trzeba pierw zamontować by uzyskać dostęp do znajdującego się na nim kontenera. To powoduje, że nie można ustawić automatycznego otwarcia przy starcie w /etc/crypttab dlatego widnieje tam opcja noauto. Jeśli nie można otworzyć kontenera na starcie, nie można też zamontować automatycznie jego systemu plików. Ale to z kolei obeszliśmy przez opcję noauto w pliku /etc/fstab . Problem jest jeszcze taki, że mimo iż kontener nie jest otwierany a jego system plików nie jest montowany to i tak podczas startu systemu jest wyrzucany błąd o braku urządzenia z UUID naszego kontenera. Opcja nofail w /etc/fstab rozwiązuje ten problem i tłumi ten komunikat.

Ale przecie ani kontener nie jest otwierany na starcie ani system plików nie jest montowany, to chyba nie tak miało wyglądać? Bynajmniej ale nadal nie jesteśmy skazani na ręczne montowanie tego zasobu. Musimy stworzyć skrypt startowy i dodać go do autostartu systemu. Tworzymy zatem plik /etc/init.d/dropbox-container o poniższej zawartości:

#! /bin/sh
### BEGIN INIT INFO
# Provides:          dropbox-container
# Required-Start:    $syslog $remote_fs
# Required-Stop:     $syslog $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Dropbox Container
# Description:       Dropbox Container
### END INIT INFO

# Author: Mikhail Morfikov    morfikov[at]gmail.com

PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin
SCRIPTNAME="dropbox_container"
CRYPT="crypt_dropbox"
MOUNT="/media/Dropbox"
VOLUME="/dev/mapper/$CRYPT"


dropbox_start () {
  /usr/sbin/cryptdisks_start $CRYPT
  /sbin/fsck.ext4 $VOLUME
  /bin/mount $MOUNT
}

dropbox_stop () {
  /bin/umount $MOUNT
  /usr/sbin/cryptdisks_stop $CRYPT
}

case "$1" in
    start)
        dropbox_start || exit 1
    ;;
    stop)
        dropbox_stop || exit 1
    ;;
    force-reload|restart)
        dropbox_stop && sleep 2
        dropbox_start || exit 1
    ;;
        *)
        echo "Usage: $SCRIPTNAME {start|stop|restart}"
        exit 1
    ;;       
esac

exit 0

Jak widać powyżej, skrypt ma zaimplementowaną obsługę sprawdzania systemu plików kontenera, także nie będziemy musieli tego robić ręcznie. Teraz dodajemy ten skrypt do autostartu przez:

# update-rc.d dropbox-container defaults

I to by było w zasadzie wszystko. Po starcie systemu kontener zostanie otworzony a jego system plików zamontowany. Przy czym taki otwarty kontener jest przez dropboxa traktowany jako plik w fazie edycji w związku z czym mogą pojawić się dziwne efekty. W każdym razie ja to sobie testowałem przez parę dni i wychodzi na to, że może i dropbox ten kontener traktuje jako w fazie edycji ale tylko przez chwilę, po czym synchronizuje go jak gdyby nigdy nic. Jedyne o czym trzeba pamiętać, to fakty by pilnować aby kontener nie był otwarty na dwóch+ maszynach jednocześnie, bo wtedy dropbox skopiuje kontener i będą dwa. :]

Jeszcze kilka słów na temat tego w jaki sposób dostęp do kontenera dropbox powinien być chroniony. Słabe hasło najlepiej jest zastąpić przez keyfile. Oczywiście sposób uzyskiwania dostępu do kontenera zostaje taki sam ale na wypadek problemów z zależną partycją musimy mieć jakieś zabezpieczenie, by dane z dropboxa nie przepadły. Najlepiej stworzyć keyfile w oparciu o plik binarny, który jest trzymany "w głębokim ukryciu". xD Głębokie ukrycie będzie polegało na wybraniu jakiegoś pliku, który rezyduje na naszym dysku ale jest już używany w jakiś sposób np. plik mp3 czy .avi. Teraz przy pomocy --new-keyfile-size= --new-keyfile-offset= możemy wskazać, od której części pliku ma się zaczynać nasz keyfile oraz jak długi ma on być. Przykład wykorzystania takiego mechanizmu:

cryptsetup luksAddKey --new-keyfile-size=2048 --new-keyfile-offset=4096 '/media/Server/Dropbox/luks_dropbox' '/media/Dane/mp3/Nickelback/(2011) - Here And Now/02 - Bottoms Up.mp3'

Jeśli teraz spróbujemy otworzyć kontener przy pomocy tej mp3:

# cryptsetup luksOpen  '/media/Server/Dropbox/luks_dropbox' crypt_dropbox --key-file='/media/Dane/mp3/Nickelback/(2011) - Here And Now/02 - Bottoms Up.mp3'
No key available with this passphrase.

A jeśli spróbujemy przestawić o 1 offset i rozmiar?


# cryptsetup luksOpen '/media/Server/Dropbox/luks_dropbox' crypt_dropbox --key-file='/media/Dane/mp3/Nickelback/(2011) - Here And Now/02 - Bottoms Up.mp3' --keyfile-offset=4095 --keyfile-size=2048
No key available with this passphrase.

# cryptsetup luksOpen '/media/Server/Dropbox/luks_dropbox' crypt_dropbox --key-file='/media/Dane/mp3/Nickelback/(2011) - Here And Now/02 - Bottoms Up.mp3' --keyfile-offset=4096 --keyfile-size=2047
No key available with this passphrase.

Powyżej został pokazany prosty przykład, jednak wartościami offset i size można dowolnie manipulować i ustawić można sobie dowolne wartości i nawet jeśli już ktoś wejdzie w posiadanie naszej mp3, to będzie musiał znać 2 parametry by odblokować kontener. Wielokrotne, samo kryjące się zabezpieczenie, to jest to czego nam potrzeba, a nie jakiś sztucznie wygenerowany keyfile przy pomocy dd. xD Możliwe jest, co prawda, zdefiniowanie tego keyfile w /etc/crypttab i użycie go do odblokowania kontenera ale ja bym tego nie robił -- można tym zdradzić położenie keyfile no i oczywiście offset i size . Lepiej zostawić ten keyfile jako backup.

W przypadku zrobienia backupu w postaci keyfile, trzeba pamiętać też o wyczyszczeniu odpowiednich slotów w nagłówku kontenera dropbox. Służy do tego cryptsetup luksKillSlot:

# cryptsetup luksKillSlot '/media/Server/Dropbox/luks_dropbox' 0
Enter any remaining passphrase:

Jeśli uważasz, że któryś z moich artykułów jest ciekawy i przydał ci się do czegoś oraz nie masz konta na dropboxie, możesz się zrewanżować zakładając konto z tego linku http://db.tt/90XFs5H [2], zawsze mi to doda trochę miejsca (500MiB up to 16GiB).


Przypisy:

  1. http://dug.net.pl/tekst/242/szyfrowanie_danych_na_dropboxie/
  2. http://db.tt/90XFs5H