Zmiana rozmiaru kontenera LUKS

Spis treści

Ci z nas, którzy zabezpieczają swoje systemy przy pomocy technik kryptograficznych wiedzą, że taki system trzeba traktować nieco inaczej niż ten, który nie jest w żaden sposób zaszyfrowany. Gdy mamy na swoim dysku kilka kontenerów LUKS (czy też TrueCrypt), problematyczna może się okazać zmiana rozmiaru tego typu partycji. Praktycznie żadne narzędzia graficzne, ewentualnie inne automaty, nie są w stanie nas przez ten proces bezstresowo przeprowadzić. Musimy zatem skorzystać z niskopoziomowych aplikacji, takich jak fdisk czy cryptsetup , by ten rozmiar sobie dostosować. Problem w tym, że nieumiejętne operowanie na tych narzędziach może skończyć się tragicznie dla zgromadzonych na dysku danych. W tym wpisie postaram się opisać cały proces zmiany rozmiaru zaszyfrowanego kontenera LUKS wliczając w to również dostosowanie partycji i jej systemu plików.

Przygotowanie kontenera LUKS

W przypadku zwykłych, niezaszyfrowanych partycji, zmiana rozmiaru nie jest niczym trudnym. Praktycznie do każdego typu systemu plików (w tym też do EXT4, FAT32, NTFS) posiadamy dedykowane narzędzia, które potrafią zmienić ich rozmiar. Wszystkie te narzędzia operują głównie na systemach plików. Z kolei do zmiany rozmiaru partycji mamy fdisk . Widzimy zatem, że struktura jest warstwowa. Najpierw jest partycja, a na niej system plików. W przypadku zaszyfrowanych partycji, dodawana jest nowa warstwa między te dwie powyższe.

W debianie, do obsługi zaszyfrowanych kontenerów LUKS wykorzystuje się narzędzia z pakietu cryptsetup-bin . Zakładam, że mamy już do dyspozycji jakiś kontener, którego rozmiar chcemy zmienić. Nie będę tutaj opisywał procesu tworzenia kontenera, bo to jest zagadnienie na osoby artykuł. Tak czy inaczej, kontener musi zostać otwarty:

# cryptsetup luksOpen /dev/sdb1 sdb1
Enter passphrase for /dev/sdb1:

# lsblk /dev/sdb
NAME             MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb                8:16   0  74.5G  0 disk
└─sdb1             8:17   0  37.3G  0 part
  └─sdb1 (dm-10) 254:10   0  37.3G  0 crypt

W zależności od tego jaki system plików posiadamy w kontenerze, trzeba skorzystać z odpowiedniego narzędzia do sprawdzenia integralności danych. W tym przypadku, w kontenerze znajduje się system plików EXT4. Zatem korzystamy z fsck.ext4 , podając mu ścieżkę do odszyfrowanego systemu plików zlokalizowanego w /dev/mapper/ , przykładowo:

# fsck.ext4 -Dvf /dev/mapper/sdb1

Zmniejszanie kontenera LUKS

Jeśli stworzyliśmy kiedyś zaszyfrowaną partycję, z której rozmiaru obecnie nie jesteśmy zadowoleni i chcielibyśmy ją zmniejszyć, to nic nie stoi na przeszkodzie, by to zrobić. Kolejność działań jest mniej więcej taka sama jak w przypadku zmiany rozmiaru zwykłej partycji. Najpierw zmieniamy rozmiar systemu plików, a potem... no właśnie, co potem? Trzeba będzie zmniejszyć rozmiar kontenera, a dopiero później przyciąć samą partycję. Zatem do dzieła, zmieniamy rozmiar systemu plików EXT4 z obecnych 37 GiB na 10 GiB przy pomocy resize2fs :

# resize2fs -p /dev/mapper/sdb1 10G
resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/mapper/sdb1 to 2621440 (4k) blocks.
Begin pass 2 (max = 78514)
Relocating blocks             XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 3 (max = 299)
Scanning inode table          XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Begin pass 4 (max = 915)
Updating inode references     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
The filesystem on /dev/mapper/sdb1 is now 2621440 blocks long.

Mając ciągle otwarty kontener ale niepodmontowany zasób, zmieniamy jego rozmiar przez cryptsetup resize . Przy czym, ten parametr przyjmuje wartości w sektorach, a wyżej mamy ilość bloków (2621440), z których każdy ma 4096 bajtów. Trzeba to przeliczyć na sektory 512 bajtowe, czyli wartość 2621440 pomnożyć przez 8 (4096/512), co daje 20971520-1=20971519 sektorów. Tylko tutaj ważna uwaga. Ta liczba bloków nie uwzględnia nagłówka zaszyfrowanej partycji. A więc jest to tylko rozmiar systemu plików. Jeśli nie wiemy ile zajmuje nagłówek naszej partycji, możemy to sprawdzić przez:

# cryptsetup status sdb1
/dev/mapper/sdb1 is active.
  type:    LUKS1
  cipher:  aes-xts-plain64
  keysize: 512 bits
  device:  /dev/sdb1
  offset:  4096 sectors
  size:    78229504 sectors
  mode:    read/write

Jest to 4096 sektorów 512-bajtowych, co daje 2097152 bajtów, 2097152/1024/1024=2 MiB . Jeśli teraz byśmy nie uwzględnili w wyliczeniach tego nagłówka i próbowali dostosować partycję obcinając te dodatkowe bloki, to przy próbie montowania dostaniemy błąd:

# mount /dev/mapper/sdb1 /mnt
mount: wrong fs type, bad option, bad superblock on /dev/mapper/sdb1,
       missing codepage or helper program, or other error
       In some cases useful info is found in syslog - try
       dmesg | tail  or so

A w syslogu poniższy komunikat:

kernel: [40192.322415] EXT4-fs (dm-10): bad geometry: block count 2621440 exceeds size of device (2620928 blocks)

Sama partycja otworzy się bez problemu, bo obcięliśmy kawałek z tyłu partycji. Jeśli przyjrzymy się bliżej temu powyższemu komunikatowi, to 2621440-2620928=512. Jest to 512 bloków, a każdy z nich ma 4096 bajtów, co daje w sumie wartość 2097152 (512*4096). A to z kolei jest 2097152/1024/1024=2 MiB, czyli tyle samo, co w przypadku ustalania wielkości nagłówka zaszyfrowanego dysku. Trzeba pamiętać, by do faktycznego rozmiaru bloków wskazanego przez resize2fs (2621440) dodać 512 bloków i dopiero w oparciu o te bloki, tj. (2621440+512)*8-1=20975615, zmienić rozmiar partycji w fdisk . Nie ma się co stresować i jeśli źle coś policzymy, to po prostu trzeba poprawić wpis w fdisk w oparciu o nowe obliczenia.

W przypadku kontenera LUKS, stosujemy sektory bez offsetu, tj. 2621440*8-1=20971519 :

# cryptsetup resize sdb1 --size 20971519

# lsblk /dev/sdb
NAME             MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb                8:16   0  74.5G  0 disk
└─sdb1             8:17   0  37.3G  0 part
  └─sdb1 (dm-10) 254:10   0    10G  0 crypt

Rozmiar kontenera sdb1 uległ zmianie ale partycja nadal ma 37 GiB. By zmienić rozmiar partycji, zamykamy kontener i odpalamy fdisk :

# cryptsetup luksClose sdb1
# fdisk /dev/sdb

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    78235647    39116800   83  Linux

Command (m for help): d
Selected partition 1

Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1): 1
First sector (2048-156299374, default 2048):
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-156299374, default 156299374): +20975615

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 83
Changed system type of partition 1 to 83 (Linux)

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
255 heads, 63 sectors/track, 9729 cylinders, total 156299375 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    20977663    10487808   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

No to teraz by sprawdzić czy wszystko działa, otwieramy kontener:

# cryptsetup luksOpen /dev/sdb1 sdb1
Enter passphrase for /dev/sdb1:

# lsblk /dev/sdb
NAME             MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb                8:16   0  74.5G  0 disk
└─sdb1             8:17   0    10G  0 part
  └─sdb1 (dm-10) 254:10   0    10G  0 crypt

Rozmiar partycji sdb1 uległ zmniejszeniu. Montujemy system plików i sprawdzamy czy zawarte w nim dane są możliwe do odczytania:

# mount /dev/mapper/sdb1 /mnt
# ls -al /mnt

Jeśli jesteśmy w stanie odczytać zawartość głównego katalogu, znaczy to nic innego jak tylko to, że proces zmniejszenia rozmiaru zaszyfrowanej partycji zakończył się sukcesem.

Powiększanie partycji LUKS

No to tak jeszcze, by w pełni wyczerpać temat zmiany rozmiaru zaszyfrowanej partycji, trzeba rozpatrzyć drugi wariant, czyli zwiększenie rozmiaru kontenera LUKS. Tak obecnie wygląda ułożenie partycji na dysku:

# lsblk /dev/sdb
NAME             MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb                8:16   0  74.5G  0 disk
└─sdb1             8:17   0    10G  0 part
  └─sdb1 (dm-10) 254:10   0    10G  0 crypt

Zamykamy kontener:

# cryptsetup luksClose sdb1

W fdisk'u zaś kasujemy starą partycję i tworzymy nową, powiedzmy +15G. Czyli nowy rozmiar kontenera wyniesie 25 GiB:

# fdisk /dev/sdb

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
139 heads, 49 sectors/track, 22948 cylinders, total 156299375 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    20973567    10485760   83  Linux

Command (m for help): d
Selected partition 1

Command (m for help): n
Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): p
Partition number (1-4, default 1):
Using default value 1
First sector (2048-156299374, default 2048):
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-156299374, default 156299374): +25G

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 83

Command (m for help): p

Disk /dev/sdb: 80.0 GB, 80025280000 bytes
139 heads, 49 sectors/track, 22948 cylinders, total 156299375 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000c741d

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048    52430847    26214400   83  Linux

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

Otwieramy kontener:

# cryptsetup luksOpen /dev/sdb1 sdb1
Enter passphrase for /dev/sdb1:

# lsblk /dev/sdb
NAME             MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sdb                8:16   0  74.5G  0 disk
└─sdb1             8:17   0    25G  0 part
  └─sdb1 (dm-10) 254:10   0    25G  0 crypt

Rozmiar partycji i kontenera LUKS uległ zmianie. Trzeba jeszcze rozszerzyć system plików. Tylko tutaj jest problem związany z nagłówkami. W fdisk'u ustawiliśmy 25G, czyli jest to 25*1024*1024*1024=26843545600 bajtów. Z kolei 26843545600/512 daje 52428800 sektorów. Co przekłada się na 52428800/2=26214400 bloków (tych w fdisku) lub 52428800/8=6553600 bloków w resize2fs . Jeśli byśmy spróbowali ustawić 25G jako rozmiar, dostaniemy błąd:

# resize2fs -p /dev/mapper/sdb1 25G
resize2fs 1.42.9 (28-Dec-2013)
The containing partition (or device) is only 6553088 (4k) blocks.
You requested a new size of 6553600 blocks.

Oczywiście można, by pójść na łatwiznę i wpisać jeszcze raz rozmiar, tym razem używając liczby 6553088, którą wypisał powyżej resize2fs . Ale my to policzymy. By system plików miał odpowiedni rozmiar, trzeba odjąć rozmiar nagłówków, a to jest 512 bloków 4096-bajtowych (512*4096=2097152). Czyli rozmiar nowego systemu plików wynieść powinien 26843545600-2097152=26841448448 bajtów. Narzędzie resize2fs nie przyjmie rozmiaru w bajtach (tylko w przypadku K,M,G). Za to przyjmuje rozmiar w sektorach. Zatem nowy rozmiar powinien wynieść 26841448448/512=52424704 sektorów. Jeśli chcielibyśmy operować na blokach: 6553600−512=6553088 bloków, 6553088*4096=26841448448 bajtów, 26841448448/512=52424704 sektorów, 52424704/8=6553088 bloków 4096-bajtowych (4096=8*512). Dokładnie tyle samo ile zasugerował resize2fs . Sprawdźmy zatem czy resize2fs przyjmie rozmiar 52424704s :

# resize2fs -p /dev/mapper/sdb1 52424704s
resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/mapper/sdb1 to 6553088 (4k) blocks.
The filesystem on /dev/mapper/sdb1 is now 6553088 blocks long.

Weszło. W przypadku, gdyby pojawił się poniższy komunikat:

# resize2fs -p /dev/mapper/sdb1 52424704s
resize2fs 1.42.9 (28-Dec-2013)
Please run 'e2fsck -f /dev/mapper/sdb1' first.

Trzeba przeskanować system plików i ponowić żądanie zmiany rozmiaru. Montujemy jeszcze system plików i sprawdzamy czy są w nim wcześniej stworzone pliki:

# mount /dev/mapper/sdb1 /mnt
# ls -al /mnt/

I to wszystko co się tyczy zmiany rozmiaru zaszyfrowanego kontenera.

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.