Ridimensionare volumi LVM


The media in this post is not displayed to visitors. To view it, please log in.

lvmSupponiamo di avere 3 volumi logici, ad es. vol1, vol2, vol3 e di voler aumentare il secondo a discapito degli altri due.

Un’esigenza analoga, su un filesystem partizionato in 3 parti in maniera canonica, è un mezzo incubo perché il ridimensionamento della partizione centrale prevede un discreto numero di salti mortali per manenere la contiguità e per non rischiare di lasciare buchetti inutilizzabili fra una partizione e l’altra.

Provo a buttare giù due righe su quello che mi verrebbe di fare:

  1. riduco la prima partizione
  2. riduco la terza partizione
  3. sposto la terza partizione fino alla fine del disco
  4. sposto la seconda partizione fino alla fine della prima partizione
  5. estendo la seconda partizione fino alla fine della prima

Tutto questo tenendo presente che l’unità minima allocabile è il blocco (512 bytes) e che l’operazione che mi fa più paura è il move della partizione. parted non ha un comando “move” diretto. La procedura richiede di calcolare i nuovi settori, spostare i dati e aggiornare la tabella delle partizioni.

Senza una GUI come quella di gparted, bisogna farsi letteralmente i conti con carta e penna prima di agire e c’è il rischio, comunque molto alto, di commettere errori che sarebbero disastrosi.

LVM, al confronto, è una boccata d’ossigeno.

LVM dà la possibilità di ridimensionare volumi in maniera più semplice rispetto al partizionamento più tradizionale perché la dimensione della partizione è disaccoppiata da concetti di contiguità e dalla geometria del disco.

Nel caso del partizionamento tradizionale infatti le partizioni sono dei blocchi di settori consecutivi in cui ogni partizione inizia in un settore finisce in un altro.

Con LVM invece l’approccio è radicalmente diverso. La minima unità allocabile è l’extent (default 4 MiB) che serve per mappare un volume fisico in un volume logico.

Se immaginiamo che il volume fisico possa essere spezzettato in altrettanti extents in una sorta di “paniere”, il volume group, il volume logico non è altro che un insieme di questi extents pescati dal volume group (senza alcuna pretesa d’ordinamento) a cui posso:

  • aggiungere extents prelevandoli dal volume group
  • levare extents riponendoli nel volume group (o assegnandoli ad altri volumi logici).

Queste proprietà conferiscono una grande flessibilità alle operazioni di riduzione ed estensione dei volumi.

1. Cose che è bene ricordare quando si manipolano i volumi logici.


PartizioniQuando si riduce o aumenta un filesystem, è bene smontare le partizioni e volumi logici.

Ridurre un volume logicoQuando si riduce un volume logico, si deve:

  1. fare un check del filesystem
  2. ridurre il filesystem
  3. ridurre il volume logico

Estendere un volume logicoQuando si aumenta un volume logico, al contrario, si deve:

  1. estendere il volume logico
  2. estendere il filesystem
  3. fare un check del filesystem

Calcolare lo spazio allocabileUn volume logico è composto da un insieme di extents, blocchi grandi di default 4 MiB, che sono la minima unità allocabile. Un volume logico è quindi sempre corrispondente ad un multiplo di 4 MiB, n extents di cui n-1 allocabili, il rimanente per i metadati. Ad es. un volume logico di 2 GiB è composta da 512 extents di cui 511 allocabili.

La potenza di dueSi deve tenere sempre presente che, nella matematica del calcolo dello spazio, si considerano le potenze di 2. Non di 10. Quindi un GiB equivale a 1024 MiB, non a 1000. Di conseguenza, se dividessi un GiB in due parti uguali avrei 2 blocchi da 512 MiB non da 500.

Estensione e riduzioneNelle istruzioni di estensione (lvextend) e riduzione (lvreduce) possiamo scegliere ciò che va specificato fra 4 modalità:

  1. il numero totale di extents
  2. il delta in aggiunta o in diminuzione degli extents (a seconde che l’operazione sia rispettivamente di estensione o di riduzione)
  3. la dimensione totale espressa in KiB-MiB-GiB-TiB
  4. come prima, il delta in aggiunta o in diminuzione della dimensione espresso in KiB-MiB-GiB-TiB


2. Scenario 1: Estensione e riduzione di volumi logici


Supponiamo di avere un disco da 2 GiB (2048 MiB) diviso in 3 volumi logici da 550 MiB, 350 MiB e 1148 MiB di e di volerne ridurre due per ampliare il terzo.

Vogliamo ridurre il primo di 150 MiB, il terzo di 330 MiB e aumentare corrispondentemente il secondo volume di 480 MiB.

Prepariamo il laboratorio col solito file appiccicato ad un loop device. Su quello definirò il volume group, il mio “paniere” di extents.

# creazione device
fallocate -l 2GiB disk_1.img

# creazione device e volum group
vgcreate vg_lab $(losetup -Pf --show disk_1.img)

# creazione volumi logici
lvcreate -n lv_lab_1 vg_lab -L 550M
lvcreate -n lv_lab_2 vg_lab -L 350M
lvcreate -n lv_lab_3 vg_lab -l 100%FREE

# formattazione volumi logici
mkfs.ext4 /dev/vg_lab/lv_lab_1
mkfs.ext4 /dev/vg_lab/lv_lab_2
mkfs.ext4 /dev/vg_lab/lv_lab_3

# mount dei volumi
mkdir vol_1 vol_2 vol_3
mount -t ext4 -o defaults /dev/vg_lab/lv_lab_1 vol_1
mount -t ext4 -o defaults /dev/vg_lab/lv_lab_2 vol_2
mount -t ext4 -o defaults /dev/vg_lab/lv_lab_3 vol_3

2.1. Step 0: curiosità


Prima di cominciare esaminiamo un po’ di dati, ad es. di quanti extents sono composti i nostri oggetti.

pvdisplay /dev/loop9
  --- Physical volume ---
  PV Name               /dev/loop9
  VG Name               vg_lab
  PV Size               2,00 GiB / not usable 4,00 MiB
  Allocatable           yes (but full)
  PE Size               4,00 MiB
  Total PE              511
  Free PE               0
  Allocated PE          511
  PV UUID               YmLOru-bIdL-iqGb-vJfI-RsTk-yhv7-vSJhkX

pvdisplay mi dà informazioni sul disco fisico che andrò ad aggiungere nel volume group. Fra queste:
  • PV Name: il nome del device, /dev/loop9
  • VG Name: è il nome del gruppo di volume, vg_lab, visibile solo perché abbiamo creato il gruppo di volume direttamente sul dispositivo invece che passare prima da pvcreate.
  • PV Size: 2 GiB, la dimensione del nostro “disco”
  • PE Size: dove PE sta Physical Extent, è di 4 MiB
  • Total PE sono i PE totali e sono 511, come previsto.


vgdisplay vg_lab
  --- Volume group ---
  VG Name               vg_lab
  System ID
  Format                lvm2
  Metadata Areas        1
  Metadata Sequence No  1
  VG Access             read/write
  VG Status             resizable
  MAX LV                0
  Cur LV                0
  Open LV               0
  Max PV                0
  Cur PV                1
  Act PV                1
  VG Size               <2,00 GiB
  PE Size               4,00 MiB
  Total PE              511
  Alloc PE / Size       0 / 0
  Free  PE / Size       511 / <2,00 GiB
  VG UUID               6D89ck-c2Ni-XlMN-5Was-rh5j-vi2t-5juCXt

vgdisplay mi dà informazioni sul gruppo di volumi. Fra queste, disitnguiamo
  • VG Name: il nome del volume group, già visto in pvdisplay, vg_lab
  • VG Size: la dimensione del volume group, minore di 2 GiB perché ci sono i metadati da considerari
  • PE Size: la dimensione di un extent, 4 MiB
  • Total PE / Size: Il numero totale di extent allocabili, che conferma VG Size, pari a 511 invece che 512.
  • Alloc PE: il numero totale di extent allocati, momento della creazione del volume group è 0
  • Free PE / Size: il numero totale di extent liberi, prima della creazione dei volumi logici è 511

Cosa succede quando creerò i volumi logici? Che cambia il numero di PE liberi e allocati. Siccome userò tutti gli extent disponibili, i valori per Alloc PE e Free PE si invertiranno rispetto a prima.

Infatti dopo la creazione dei volumi logici, vgdisplay mi dirà:

vgdisplay vg_lab
  --- Volume group ---
  VG Name               vg_lab
  ...
  Alloc PE / Size       511 / <2,00 GiB
  Free  PE / Size       0 / 0
  ...  

511 extents allocati come confermato dai dati desumibili dei 3 volumi logici
lvdisplay vg_lab
 --- Logical volume ---
  LV Path                /dev/vg_lab/lv_lab_1
  LV Name                lv_lab_1
  VG Name                vg_lab
...
  LV Size                552,00 MiB
  Current LE             138
...
 --- Logical volume ---
  LV Path                /dev/vg_lab/lv_lab_2
  LV Name                lv_lab_2
  VG Name                vg_lab
...
  LV Size                352,00 MiB
  Current LE             88
...
 --- Logical volume ---
  LV Path                /dev/vg_lab/lv_lab_3
  LV Name                lv_lab_2
  VG Name                vg_lab
...
  LV Size                1,11 GiB
  Current LE             285
...

Per il primo, il secondo e il terzo volume logico abbiamo rispettivamente:
  • 138, 88, 285 extents corrispondenti a
  • 552 MiB, 352 MiB e 1140 MiB (1,11 GiB)
  • per un totale di 2044 MiB, al netto dei metadati.

Possiamo notare subito che le dimensioni non corrispondono a quanto indicato in lvcreate.

lvcreate -n lv_lab_1 vg_lab -L 550M
lvcreate -n lv_lab_2 vg_lab -L 350M
lvcreate -n lv_lab_3 vg_lab -l 100%FREE

Questo succede perché vengono sempre approssimate all’extent più vicino. Ecco perché il primo e il secondo volume sono diventati di 552 (138 extents) e 352 MiB (88 extents).

Sempre ricordando la particolarità dei multipli di 4 MiB legati a LVM, anche la riduzione di 150 MiB e di 330 MiB dei due volumi saranno approssimate sempre all’extent più vicino (152 MiB=38 extents e 332=83 extents). Questo dettaglio si rivelerà fondamentale quando dovremo ridimensionare il filesystem.

Con questa rinnovata consapevolezza cominciamo a ridimensionare.

2.2. Step 1: Smontare i dischi

umount /dev/vg_lab/lv_lab_1
umount /dev/vg_lab/lv_lab_2
umount /dev/vg_lab/lv_lab_3

2.3. Step 2: Riduzione del filesystem


Se dovessi ridimensionare solo il filesystem, sarebbe sufficiente considerare dimensioni che siano potenze di 2 e non di 10.

Ma sapendo che sotto c’è un LVM, sappiamo che per mantenere l’allineamento fra filesystem e volumi logici, oltre che potenze di due le dimensioni devono essere anche multipli di 4MiB.

Ecco perché anche nel resize reale del filesystem non dovrò levare 150 MiB e 330 MiB ma 148 MiB (37 PE) e 328 (82 PE) (è buona norma approssimare per difetto all’extent più vicino per maggior prudenza) per un totale effettivo di 476 MiB (119 PE)

Il filesystem del primo volume sarà dunque di 552 MiB – 148 MiB = 404 MiB. Il filesystem del terzo volume sarà di 1140 MiB – 328 MiB = 812 MiB.

# check dei filesystem
e2fsck -f /dev/vg_lab/lv_lab_1
e2fsck -f /dev/vg_lab/lv_lab_2
e2fsck -f /dev/vg_lab/lv_lab_3

# Riduco il primo filesystem di 148 MiB
resize2fs /dev/vg_lab/lv_lab_1 404M

# Riduco il terzo filesystem di 328 MiB
resize2fs /dev/vg_lab/lv_lab_3 812M

2.4. Step 3: Ridimensionare i volumi logici


La riduzione del volume logico può essere fatta in 4 modi come sappiamo, ad es. sul primo volume:

# il numero totale di extents, 138 PE - 37 PE = 101 PE
lvreduce -l 101 /dev/vg_lab/lv_lab_1 #oppure

# il numero di exrtents da sottrarre, 38 PE
lvreduce -l -37 /dev/vg_lab/lv_lab_1 #oppure

# la dimensione totale da ottenere, 404 MiB
lvreduce -L 404M /dev/vg_lab/lv_lab_1 #oppure

# il numero di MiB da sottrarre, 148 MiB
lvreduce -L -148M /dev/vg_lab/lv_lab_1 #oppure

Per maggior chiarezza userò il size assoluto in modo da farlo corrispondere a resize2fs
# Riduco il primo volume logico di 148 Mib
lvreduce -L 404M /dev/vg_lab/lv_lab_1

# Riduco il terzo volume logico di 328 Mib
lvreduce -L 812M /dev/vg_lab/lv_lab_3

Verifichiamo quanti siano i PE residui
vgdisplay vg_lab | grep "Free  PE"
 Free  PE / Size       119 / 476,00 MiB

119 extents, come previsto.

2.5. Step 4: Estendere il volume logico


Dopo aver ridotto il primo e il terzo volume, non ci rimane che estendere il secondo in base alla scaletta indicata prima:

  1. si estende il secondo volume logico
  2. si estende il filesystem
  3. si esegue il check fnale del filesystem

L’estensione del volume, come ormai ben sappiamo, non sarà di 480 MiB ma di 476 MiB (37 PE + 82 PE = 119 PE) per via degli arrotondamenti effettuati nella riduzione degli altri volumi logici.

# specifico gli extent che so essere residui
lvextend -l +119 /dev/vg_lab/lv_lab_2
# o, equivalentemente
# lvextend -L +476M /dev/vg_lab/lv_lab_2

# estendo il filesystem
resize2fs /dev/vg_lab/lv_lab_2 828M

# check filesystem
e2fsck -f /dev/vg_lab/lv_lab_2

2.6. Step 5: Bonus


Come premio per essere arrivato in fondo, posso rivelare come fare in un colpo solo tutte le operazioni descritte sopra.

È vero che c'è poco da considerare, giusto tenere a mente che su LVM ogni oggetto è multiplo di 4 MiB e che bisogna ridimensionare filesystem e volumi logici in ugual modo per non generare pericolose anomalie, ma tutte le operazioni che ho descritto nei passi 2-4 possono essere fatte con un unico comando che provvederà ad eseguire nell'ordine corretto e con le giuste approssimazioni:

  • il check del filesystem
  • il ridimensionamento del filesystem
  • il ridimensionamento dei volumi logici

Dunque, tutto il pippone atomico precedente può essere condensato in un unico, solido comando:

# Riduce il primo volume logico e il filesystem
lvreduce -r -L -150M /dev/vg_lab/lv_lab_1

# Riduce il terzo volume logico e il filesystem
lvreduce -r -L -330M /dev/vg_lab/lv_lab_3

# Estende il secondo volume logico e il filesystem
lvextend -r -l +119 /dev/vg_lab/lv_lab_2

Ora spieghiamo il perché soprattutto dell'extend, che è interessante.

Diciamo che è tutto molto guidato e le eventuali correzioni da apportare, senza spaccarsi troppo il cervello, sono suggerite con estrema chiarezza, basta leggere.

lvreduce -r -L -150M /dev/vg_lab/lv_lab_1
 Rounding size to boundary between physical extents: 148.00 MiB.
  File system ext4 found on vg_lab/lv_lab_1.
  File system size (552.00 MiB) is larger than the requested size (404.00 MiB).
  File system reduce is required using resize2fs.
...
  Size of logical volume vg_lab/lv_lab_1 changed from 552.00 MiB (138 extents) to 404.00 MiB (101 extents).
  Logical volume vg_lab/lv_lab_1 successfully resized.

Ci dice innanzitutto che:
  • c'è stato un arrotondamento a 148 MiB (37 extents);
  • il filesystem è più grande del volume richiesto pertanto va subito ridimensionato;
  • infine si ridimensiona il volume logico da 552 MiB (138 extents) a 404 MiB (101 extents).

Anche il secondo lvreduce ha un risultato analogo

lvreduce -r -L -330M /dev/vg_lab/lv_lab_3
  Rounding size to boundary between physical extents: 328.00 MiB.
  File system ext4 found on vg_lab/lv_lab_3.
  File system size (1.11 GiB) is larger than the requested size (812.00 MiB).
  File system reduce is required using resize2fs.
...
  Size of logical volume vg_lab/lv_lab_3 changed from 1.11 GiB (285 extents) to 812.00 MiB (203 extents).
  Logical volume vg_lab/lv_lab_3 successfully resized.

  • anche qui abbiamo un arrotondamento a 328 MiB (82 extents);
  • il filesystem è più grande del volume richiesto pertanto va subito ridimensionato;
  • infine si ridimensiona il volume logico da 1140 MiB (285 extents) a 812 MiB (203 extents).

È facilissimo desumere che la massima dimensione dell'estensione sia 148 MiB + 328 MiB = 476 MiB (119 extents), basta fare una somma.

Ma supponiamo di essere particolarmente distratti e proviamo ad estendere considerando le quantità iniziali: 150 MiB + 330 MiB = 480 MiB

lvextend -r -L +480M /dev/vg_lab/lv_lab_2
  File system ext4 found on vg_lab/lv_lab_2.
  File system fsck will be run before extend.
  Insufficient free space: 120 extents needed, but only 119 available

Nonostante la mia distrazione, come si può vedere, non si producono danni perché l'ouput è categorico: “non faccio nulla. Se vuoi, puoi aumentare al max di 476 MiB (119 extents)”. Non ci sono danni e anzi c'è pure il suggerimento risolutivo.

Ed ecco spiegato il mio extend di prima.

3. Scenario 2: Estensione e riduzione di gruppi di volume


Esaminiamo le possibilità di aggiungere o rimuovere dispositivi ad un volume group esistente.

Ricreiamo il laboratorio partendo da 3 dischi, poi aggiungeremo due nuovi dischi e ne rimuoveremo altrettanti, il tutto senza intaccare l'integrità dei dati.

# creazione di 5 device
for i in {1..9}: do fallocate -l 1GiB disk_${i}.img; done

# creazione del gruppo di volumi con 3 device
vgcreate vg_lab \
  $(losetup -Pf --show disk_1.img) \
  $(losetup -Pf --show disk_2.img) \
  $(losetup -Pf --show disk_3.img)

# crezione di 3 volumi logici
lvcreate -n lv_lab_1 vg_lab -l 300
lvcreate -n lv_lab_2 vg_lab -l 250
lvcreate -n lv_lab_3 vg_lab -l 100%FREE

# formattazione dei 3 dispositivi
mkfs.ext4 /dev/vg_lab/lv_lab_1
mkfs.ext4 /dev/vg_lab/lv_lab_2
mkfs.ext4 /dev/vg_lab/lv_lab_3

Ecco come sono distribuiti i volumi logici all'interno dei dischi fisici
lsblk -o NAME,FSTYPE,SIZE,TYPE
  NAME               FSTYPE        SIZE      TYPE
  loop9              LVM2_member     1G      loop
  └─vg_lab-lv_lab_1                1,2G      lvm
  loop10             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_1                1,2G      lvm
  └─vg_lab-lv_lab_3                860M      lvm
  loop11             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_2               1000M      lvm
  └─vg_lab-lv_lab_3                860M      lvm

Il gruppo di volumi è composto da 3 volumi fisici tutti attivi
vgdisplay vg_lab |grep PV
  Max PV                0
  Cur PV                3
  Act PV                3

Andiamo ad estendere il nostro gurppo di volumi con altri due device
vgextend vg_lab $(losetup -Pf --show disk_4.img) $(losetup -Pf --show disk_5.img)

I nuovi dischi sono visibili in fondo, come si può vedere anche da vgdisplay che mostra i 5 dischi tutti attivi.
lsblk -o NAME,FSTYPE,SIZE,TYPE
  NAME               FSTYPE        SIZE      TYPE
  loop9              LVM2_member     1G      loop
  └─vg_lab-lv_lab_1                1,2G      lvm
  loop10             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_1                1,2G      lvm
  └─vg_lab-lv_lab_3                860M      lvm
  loop11             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_2               1000M      lvm
  └─vg_lab-lv_lab_3                860M      lvmchan
  loop12             LVM2_member     1G      loop
  loop13             LVM2_member     1G      loop

vgdisplay vg_lab |grep PV
  Max PV                0
  Cur PV                5
  Act PV                5

Potremo usare i nuovi dischi per estendere i volumi logici esistenti ma li impiegheremo invece per rimpiazzare i primi due dischi.

pvmove distribuisce tutti gli extents del disco fra tutti i volumi fisici che hanno spazio a sufficienza. Se non dovesse essercene, restituirà un messaggio d'errore.

pvmove /dev/loop9
  /dev/loop9: Moved: 3,14%
  /dev/loop9: Moved: 100,00%

Alla fine dell'operazione il disco s'è liberato di tutti i suoi extents e può essere rimosso
lsblk -o NAME,FSTYPE,SIZE,TYPE
  NAME               FSTYPE        SIZE      TYPE
  loop9              LVM2_member     1G      loop
  loop10             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_1                1,2G      lvm
  └─vg_lab-lv_lab_3                860M      lvm
  loop11             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_2               1000M      lvm
  └─vg_lab-lv_lab_3                860M      lvm
  loop12             LVM2_member     1G      loop
  └─vg_lab-lv_lab_1                1,2G      lvm
  loop13             LVM2_member     1G      loop

Prima si estrae il volume fisico dal gruppo di volumi e poi si rimuove il volume fisico e così può essere scollegato.
vgreduce vg_lab /dev/loop9
pvremove /dev/loop9

Verifichiamo che il volume fisico non sia più presente.
vgdisplay vg_lab |grep PV
  Max PV                0
  Cur PV                4
  Act PV                4

Procediamo allo stesso modo col secondo disco.
pvmove /dev/loop10
  /dev/loop10: Moved: 9,80%
  /dev/loop10: Moved: 17,65%
  /dev/loop10: Moved: 100,00%

vgreduce vg_lab /dev/loop10
  Removed "/dev/loop10" from volume group "vg_lab"

pvremove /dev/loop10
  Labels on physical volume "/dev/loop10" successfully wiped.

In conclusione possiamo vedere il gruppo di volumi con solo 3 dischi, gli altri due completamente disimpegnati col gruppo di volumi ricostituitosi attorno ai 3 dischi rimanenti. E tutto spostando semplicemente gli extents dove c'era disponibilità in maniera totalmente trasparente per il filesystem.
vgdisplay vg_lab |grep PV
  Max PV                0
  Cur PV                3
  Act PV                3


lsblk -o NAME,FSTYPE,SIZE,TYPE
  NAME               FSTYPE        SIZE      TYPE
  loop9                              1G      loop
  loop10                             1G      loop
  loop11             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_2               1000M      lvm
  └─vg_lab-lv_lab_3                860M      lvm
  loop12             LVM2_member     1G      loop
  └─vg_lab-lv_lab_1                1,2G      lvm
  loop13             LVM2_member     1G      loop
  ├─vg_lab-lv_lab_1                1,2G      lvm
  └─vg_lab-lv_lab_3                860M      lvm

4. Conclusione


Ho solo sfiorato la complessità e le capacità offerte da LVM.

L'estensione e la riduzione di volumi logici e di gruppi di volumi sono scenari di base. Tuttavia sono sufficienti per mostrare come sia semplice, con i volumi logici, compiere operazioni che con un filesystem partizionato in maniera classica sarebbero complicatissime.

#lvm #volumegroup #logicalvolume #filesystem #devicemapper


noblogo.org/aytin/ridimensiona…

Device Mapper: LUKS + LVM


The media in this post is not displayed to visitors. To view it, please log in.

luks-lvm(segue da Cenni sulla creazione di pool di storage con LVM)...anche se è la base per una serie di sviluppi interessanti.

Come ormai sappiamo, device mapper è il framework del kernel Linux col quale mappare dispositivi a blocchi fisici su dispositivi a blocchi logici, che costituisce la base per fornire funzionalità ulteriori quali:

  • volumi logici
  • raid
  • cifratura (Full Disk Encryption)
  • snapshot di volumi


Scenario 1


È molto comune per es. cifrare l'intero disco e “affettarlo” con volumi logici in base alle proprie esigenze di partizionamento .

Il doppio vantaggio è dato da:

  1. offuscamento totale dello schema di partizionamento
  2. estrema versatilità / flessibilità del partizionamento grazie ai volumi logici

Come si agisce?

  1. si cifra il dispositivo fisico
  2. si crea un gruppo di volumi avente come volume fisico il volume cifrato
  3. si creano i volumi logici in base allo schema di partizionamento desiderato.

luks-lvm

Passo 1 – Inizializzazione


Simulo il mio dispositivo fisico ricorrendo ai loop device.

Il “disco” avrà una grandezza simbolica di 2 GiB, non avrà header detachable. L'algoritmo di hash sarà sha512 e la chiave sarà da 512 bit. Il resto è il default di luks2 (argon2id come pbkdf, per maggiori dettagli vedi cryptsetup --help)

# 1. Preparazione disco "fisico"
fallocate -l 2g cipher_disk.img

# 2. loop device che simula l'attach del dispositivo
DEV=$(losetup -Pf --show cipher_disk.img)

# 3. Inizializzazione cifratura
cryptsetup luksFormat  \
    --type luks2 \
    --hash sha512 \
    --key-size 512 \
    $DEV

Passo 2


Il passo successivo consiste nell'apertura del dispositivo cifrato e nella definizione dello schema di partizionamento in volumi logici

# 1. apertura del disco cifrato
cryptsetup open \
    --type luks2 \
    $DEV cipher_disk

Nell'apertura, device mapper fa la sua prima magia.

Infatti, se osserviamo lo stato dei dispositivi, vedremo una situazione simile:

lsblk
...
loop9                7:9    Kib0     2G  0 loop  
└─cipher_disk      252:3    0     2G  0 crypt 
...

Cio vuol dire che sopra il dispositivo fisico, /dev/loop9in questo caso, device mapper ha “poggiato” cipher_disk (/dev/mapper/cipher_disk).

Questo sarà il nostro volume fisico per definire gruppi di volume e, conseguentemente, i volumi logici.

# 2. creazione gruppo di volumi
vgcreate vg_lab /dev/mapper/cipher_disk

# 3. creazione volumi logici
lvcreate -n lv_lab_1 vg_lab -L 700M
lvcreate -n lv_lab_2 vg_lab -L 600M
lvcreate -n lv_lab_3 vg_lab -l 100%FREE

La situazione dei dispositivi è ora questa:
lsblk
...
loop9                7:9    0     2G  0 loop  
└─cipher_disk      252:3    0     2G  0 crypt
  ├─vg_lab-lv_lab_1 252:4    0   700M  0 lvm
  ├─vg_lab-lv_lab_2 252:5    0   600M  0 lvm
  └─vg_lab-lv_lab_3 252:6    0   728M  0 lvm
...

Sopra /dev/loop9 c'è cipher_disk (/dev/mapper/cipher_disk) e sopra di esso, i 3 volumi logici
  • lv_lab_1 (/dev/mapper/vg_lab-lv_lab_1)
  • lv_lab_2 (/dev/mapper/vg_lab-lv_lab_2)
  • lv_lab_3 (/dev/mapper/vg_lab-lv_lab_3)

Formatto e monto i volumi logici

# 4. formattazione dei 3 volumi logici
mkfs.ext4 /dev/mapper/vg_lab-lv_lab_1
mkfs.ext4 /dev/mapper/vg_lab-lv_lab_2
mkfs.ext4 /dev/mapper/vg_lab-lv_lab_3

# 5. creo e preparo i punti di mmontaggio
mkdir disk_1 disk_2 disk_3
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_1 disk_1
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_2 disk_2
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_3 disk_3
chown $USER disk_1 disk_2 disk_3

# 6. unmount di tutti i dispositivi
umount disk_1 disk_2 disk_3
vgchange -an vg_lab
cryptsetup close cipher_disk
losetup -d $DEV

Passo 3


Infine, per completezza, gli script di mount e unmount del dispositivo.mount

# 1. attach del dispositivo
DEV=$(losetup -Pf --show cipher_disk.img)

# 2. Apre il disco cifrato 
cryptsetup open \
    --type luks2 \
    $DEV cipher_disk

# 3. monta il gruppo di volume
# (opzionale. L'apertura del disco cifrato dovrebbe montare 
# automaticamente il gruppo di volumi)
vgchange -ay vg_lab

# 4. monta i volumi logici
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_1 disk_1
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_2 disk_2
mount -t ext4 -o user,noauto,rw  /dev/mapper/vg_lab-lv_lab_3 disk_3

unmount
# 1. smonta i 3 dischi
umount disk_1 disk_2 disk_3

# 2. smonta il gruppo di volumi
vgchange -an vg_lab

# 3. chiude il disco cifrato
cryptsetup close cipher_disk

# 4. "stacca" il dispositivo fisico
losetup -d $DEV

Questo è ciò che si farebbe normalmente quando si vuole il full disk encryption sul proprio pc. Ma con una piccola eccezione.

In realtà ciò che viene cifrata è una partizione quasi completa del disco, perché almeno una piccola partizione, quella contenente il boot, /BOOT, deve essere in chiaro per consentire:

  • a UEFI di avviare GRUB che conosce le partizioni,
  • GRUB provvederà all'avvio del kernel,
  • l'avvio del kernel con initramfs chiederà la password per sbloccare la partizione cifrata.

La cifratura totale (che proprio totale non sarà perché /boot/efi deve rimanere in chiaro) eleva di molto la complessità del setup iniziale.

Affidare a GRUB la gestione della cifratura potrebbe voler dire, oltre alla complessità della configurazione iniziale che deve far ricorso a moduli come cryptodisk, che bisogna rinunciae ad Argon2 perché GRUB ancora non lo supporta pienamente e, a differenza del kernel, non ha sufficiente potenza per farlo lavorare come si deve.

Un buon compromesso potrebbe essere il ricorso ad un dispositivo esterno, es. una pendrive, che contenga tutta la partizione /boot in chiaro, anche l'header del disco cifrato. GRUB dovrà solo sapere dove si trovi il boot, il resto dell'avvio viene affidato come di consueto al kernel.

Scenario 2


Rimanendo nell'ambito dell'esplorazione di device mapper e della cifratura di dispositivi esterni non avviabili, immaginiamo qualcosa di più estremo.

Supponiamo di dover custodire un segreto in qualcosa che non sia un semplice vault cifrato.

Per diminuire il rischio di compromettere un unico vault, decido di dividerlo in varie parti, come gli horcrux ma con 0 malignità. Ogni parte sarà cifrata con una sua chiave che affiderò ad una persona diversa, di mia fiducia. Solo io, titolare ultimo del segreto, avrò accesso ai dati e solo la mia chiave (come l'Anello che li domina tutti) aprirà il vault.

Supponiamo di avere 3 dispositivi fisici (che nel laboratoro saranno simulati da loop device come al solito) per altrettanti “custodi”, ognuno dei quali verrà cifrato con la chiave e con dei parametri da consegnare al “custode” specifico.

I 3 dispositivi cifrati costituiranno un gruppo di volumi con un volume logico (dai requisiti posti non c'è necessità di sfruttare la flessibilità di partizionamento dei volumi logici) che verrà cifrato con la mia chiave master.

luks-lvm-luks

Ogni dispositivo, volume logico finale compreso, prima della cifratura, verrà inizializzato con del rumore casuale. Questo per impedire ad un'analisi forense di risalire ad un qualunque pattern sul dispositivo raw.

Caratteristiche di ogni cifratura:

  • dispositivi inizializzati con rumore casuale
  • header detachable
  • offset custom
  • key-file
  • default di argon2id (controlla il tuo default con cryptsetup benchmark)

Quando vorrò aprire il vault, sarà necessario che i 3 “custodi” aprano il loro “pezzo” e solo io potrò ricostruire e decifrare il volume con la mia chiave.

L'inzializzazione con rumore casuale dei dispositivo, tralasciando l'uso di dd su /dev/urandom che sappiamo essere CPU-intensive, può essere fatta ricorrendo:


################################
# Emulazione dei device fisici #
################################
fallocate -l 512M cipher_disk_1.img
fallocate -l 512M cipher_disk_2.img
fallocate -l 512M cipher_disk_3.img



###########################
# creazione dei 3 keyfile #
###########################
# keyfile del custode n° 1
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o cipher_disk_1.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -

# keyfile del custode n° 2
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o cipher_disk_2.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -

# keyfile del custode n° 3
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o cipher_disk_3.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -

# keyfile master
dd if=/dev/urandom bs=1024 count=4 | \
    gpg --yes -o master.key.gpg -c \
        --s2k-mode 3 \
        --s2k-count 32505856 \
        --s2k-cipher-algo aes256 \
        --s2k-digest-algo sha512 \
        --force-mdc -



##########################
# cifratura dei 3 device #
##########################
# attach dispositivo
DEV_1=$(losetup -Pf --show cipher_disk_1.img)

# inizializzazione dev_1 con rumore casuale
cryptsetup open --type plain ${DEV_1} container --key-file /dev/urandom
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# cifratura dispositivo n° 1
gpg -d cipher_disk_1.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_1.img \
		--offset 32768 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        ${DEV_1}

# attach dispositivo
DEV_2=$(losetup -Pf --show cipher_disk_2.img)

# inizializzazione dev_2 con rumore casuale
cryptsetup open --type plain ${DEV_2} container --key-file /dev/urandom
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# cifratura dispositivo n° 2
gpg -d cipher_disk_2.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_2.img \
		--offset 36864 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        ${DEV_2}

# attach dispositivo
DEV_3=$(losetup -Pf --show cipher_disk_3.img)

# inizializzazione dev_3 con rumore casuale
cryptsetup open --type plain ${DEV_3} container --key-file /dev/urandom
dd if=/dev/zero of=/dev/mapper/container status=progress
cryptsetup close container

# cifratura dispositivo n° 3
gpg -d cipher_disk_3.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_3.img \
		--offset 40960 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        ${DEV_3}



##############################
# Creazione gruppo di volumi #
##############################
# "apro" il volume 1
gpg -d cipher_disk_1.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_1.img \
         --key-file - \
         ${DEV_1} cipher_disk_1

# "apro" il volume 2
gpg -d cipher_disk_2.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_2.img \
         --key-file - \
         ${DEV_2} cipher_disk_2

# "apro" il volume 3
gpg -d cipher_disk_3.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_3.img \
         --key-file - \
         ${DEV_3} cipher_disk_3

# Creo il mio gruppo di volumi con i 3 volumi "fisici":
# 1. /dev/mapper/cipher_disk_1
# 2. /dev/mapper/cipher_disk_2
# 3. /dev/mapper/cipher_disk_3
vgcreate vg_master /dev/mapper/cipher_disk_1  /dev/mapper/cipher_disk_2  /dev/mapper/cipher_disk_3

# creazione dell'unico volume logico
lvcreate -n lv_master vg_master -l 100%FREE



################################
# cifratura e mount del master #
################################
# cifratura dispositivo master
dd if=/dev/urandom of=/dev/mapper/vg_master-lv_master bs=1M count=32 status=progress
gpg -d master.key.gpg | \
    cryptsetup luksFormat  \
        --type luks2 \
        --key-file - \
        --header header_master.img \
		--offset 65536 \
        --hash sha512 \
        --key-size 512 \
        --cipher aes-xts-plain64 \
        /dev/mapper/vg_master-lv_master

# apriamo il dispositivo master
gpg -d master.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_master.img \
         --key-file - \
         /dev/mapper/vg_master-lv_master cipher_disk_master

# e finalmente lo formattiamo
mkfs.ext4 /dev/mapper/cipher_disk_master

# Test: montiamo il disco
mkdir -p /run/media/master/disk_master
chown -R ${USER}:${USER} /run/media/master/disk_master
mount -t auto /dev/mapper/cipher_disk_master /run/media/master/disk_master

# Infine chiudiamo tutto
umount /run/media/master/disk_master
cryptsetup close cipher_disk_master
vgchange -an vg_master
cryptsetup close cipher_disk_1
cryptsetup close cipher_disk_2
cryptsetup close cipher_disk_3
losetup -d ${DEV_1} ${DEV_2} ${DEV_3}

Dopo aver appurato che tutto funzioni, consegno ad ogni “custode” dispositivo, keyfile e header.

Se un attaccante dovesse entrare in possesso di uno o più dispositivi, troverebbe solo un mucchio di dati incomprensibili.

Posto che riuscisse a decifrare il dispositivo, troverebbe un pezzo di un gruppo di volumi, cifrato e inutilizzabile.

Il master a questo punto non dovrà fare altro che aprire e chiudere il vault, dopo aver riunito tutti i pezzi, come segue:

Apertura del vault

# Attach dei dispositivi
DEV_1=$(losetup -Pf --show cipher_disk_1.img)
DEV_2=$(losetup -Pf --show cipher_disk_2.img)
DEV_3=$(losetup -Pf --show cipher_disk_3.img)

# "apro" il volume 1
gpg -d cipher_disk_1.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_1.img \
         --key-file - \
         ${DEV_1} cipher_disk_1

# "apro" il volume 2
gpg -d cipher_disk_2.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_2.img \
         --key-file - \
         ${DEV_2} cipher_disk_2

# "apro" il volume 3
gpg -d cipher_disk_3.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_3.img \
         --key-file - \
         ${DEV_3} cipher_disk_3

# (facoltativo) apre il gruppo di volumi
vgchange -ay vg_master

# "apre" il master volume
gpg -d master.key.gpg | \
    cryptsetup open \
         --type luks2 \
         --header header_master.img \
         --key-file - \
         /dev/mapper/vg_master-lv_master cipher_disk_master

# Monta il volume
mount -t auto /dev/mapper/cipher_disk_master /run/media/master/disk_master

Chiusura del vault
# smonta il volume cifrato
umount /run/media/master/disk_master

# chiusura del vault master
cryptsetup close cipher_disk_master

# chiusura del gruppo di volumi
vgchange -an vg_master

# chiusura dei singoli vault cifrati
cryptsetup close cipher_disk_1
cryptsetup close cipher_disk_2
cryptsetup close cipher_disk_3

# deattach dei dispositivi
losetup -d ${DEV_1} ${DEV_2} ${DEV_3}

#cryptsetup #devicemapper #dmcrypt #gpg #loseup #luks #lvm #loopdevice #storage

noblogo.org/aytin/device-mappe…