LUKS2の拡張方法

LUKS2の拡張方法

数年前に読んだガイドでは、LUKS1 がデータ損失なしで右側と隣接する空き領域に拡張する方法について説明されていましたが、左側には拡張できませんでした。最近、LUKS2 が多くの新機能とともに登場しましたが、データ損失なしで左側に拡張することに違いはありますか?

答え1

要約まだ、実際には不可能です。メタデータ形式はそれが可能であることを示唆していますが、今のところ、実際には実装されていません。したがって、古き良き「すべてのデータを再配置する」か、「新しいパーティションを作成して LVM に任せる」方法に固執する必要があります。これは、LUKS1 やほとんどのファイルシステムで行っていることと同じです。


LUKS2 ヘッダーという概念があるデータセグメント:

セグメント オブジェクトには、ユーザー データ (LUKS1 ではユーザー データ ペイロードとして言及されている) を含むディスク上の暗号化された領域の定義が含まれています。通常の LUKS デバイスの場合、データ セグメントは 1 つだけ存在します。

データの再暗号化中、データ領域は新しいキーと古いキーに従って内部的に分割されますが、ユーザーに表示されるのは抽象化された領域 1 つだけです。

データセグメントLUKS ヘッダーを左に移動し、元のデータ (最初のセグメント) を指したままにして、空き領域 (ヘッダーとデータ セグメントの間) を新しいデータ セグメントとして使用し、暗号デバイスの末尾に論理的に追加することが可能になります。LVM が任意の物理エクステントのグループを論理ボリュームに追加するのと同じです。

しかし、それは単なる理論です。

現時点では、この機能を実装する実用的な使用法やツールはなく、そのような計画があるかどうかもわかりません。


私は、型破りな方法(概念実証のようなもの)でそれを動作させようとしました。

# truncate -s 100M foobar.img
# cryptsetup luksFormat --type=luks2 foobar.img
# cryptsetup luksOpen foobar.img foobar
# yes > /dev/mapper/foobar
# sync
# hexdump -C /dev/mapper/foobar
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
05400000

LUKS2 暗号化された yes パターン。データ セグメントはどのようになっていますか?

# cryptsetup luksDump foobar.img
Data segments:
  0: crypt
    offset: 16777216 [bytes]
    length: (whole device)
    cipher: aes-xts-plain64
    sector: 512 [bytes]
# strings -n 100 foobar.img
"segments":{"0":{"type":"crypt","offset":"16777216","iv_tweak":"0","size":"dynamic","encryption":"aes-xts-plain64","sector_size":512}}

後者は、実際の生の LUKS2 メタデータの外観です。これは JSON 形式です。これは後で編集します...

左に(100Mずつ)拡大してみましょう。

# truncate -s 100M barfoo.img
# cat foobar.img >> barfoo.img
# ls -lh *.img
-rw-r--r-- 1 root root 200M Jun  9 20:57 barfoo.img
-rw-r--r-- 1 root root 100M Jun  9 20:53 foobar.img

したがって、barfoo.img は 100 MB の新しい空き領域で、その後に 100 MB の元の LUKS コンテナが続きます。

LUKS ヘッダーをデバイスの新しい先頭に再配置します。

# dd bs=1M count=16 if=foobar.img of=barfoo.img conv=notrunc
# cryptsetup luksOpen barfoo.img barfoo
# hexdump -C -n 16 /dev/mapper/barfoo
00000000  4e a6 39 e7 e0 e8 63 ae  81 72 29 81 5f 1b 08 c2  |N.9...c..r)._...|
00000010

今は開けられますが、データセグメントまだ古いオフセット(16MiB)を指していますが、もちろんそこには存在しません。100Mを追加したので、このオフセットはデータセグメント現在は116MiBになっているはずです。

編集します (オフセット 16777216 からオフセット 121634816):

# strings -n 100 barfoo.img | head -n 1 > barfoo.json
# nano -w barfoo.json
# dd if=barfoo.json of=barfoo.img bs=1 seek=4096 conv=notrunc
# dd if=barfoo.json of=barfoo.img bs=1 seek=20480 conv=notrunc

結果:

# cryptsetup luksDump barfoo.img
Device barfoo.img is not a valid LUKS device.

ああ、もちろんです。LUKS2 にはメタデータ チェックサムも搭載されています。nano で編集したり、dd でヘッダーを壊したりする必要はありません。魔法は不要です。まあ、チェックサムにパッチを当てるまでは...

# cryptsetup luksDump barfoo.img --debug
# LUKS2 header version 2 of size 16384 bytes, checksum sha256.
# Checksum:8552bf514ab70b53e63180e9fdd3bb59db1385e3dca87f792f8197b33b851aa1 (on-disk)
# Checksum:e6f322921feae0193bcbc4cddc23b87b7f192266b4a2ef34847580fd7ca18a3e (in-memory)
# LUKS2 header checksum error (offset 0).

...基本的に、ここではディスク上のチェックサムをメモリ内のチェックサムに置き換えます。

# echo e6f322921feae0193bcbc4cddc23b87b7f192266b4a2ef34847580fd7ca18a3e |
  xxd -r -ps - |
  dd of=barfoo.img bs=1 seek=448 conv=notrunc

結果(本当に):

# cryptsetup luksDump barfoo.img
Data segments:
  0: crypt
    offset: 121634816 [bytes]
    length: (whole device)
    cipher: aes-xts-plain64
    sector: 512 [bytes]
# hexdump -C /dev/mapper/barfoo
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
05400000

この時点で、LUKS ヘッダーを 100M 左に移動できました。ただし、デバイス サイズはまだ同じです... ここで、別のデータ セグメントを追加できますか? オフセット 16M から 116M まで、100M の空きセグメントがあります。これを JSON に追加しましょう。

# nano -w barfoo.json
man this is unreadable
# jq < barfoo.json > barfoo.pretty
# nano -w barfoo.pretty
...
  "segments": {
    "0": {
      "type": "crypt",
      "offset": "121634816",
      "iv_tweak": "0",
      "size": "88080384",
      "encryption": "aes-xts-plain64",
      "sector_size": 512
    },
    "1": {
      "type": "crypt",
      "offset": "16777216",
      "iv_tweak": "172032",
      "size": "104857600",
      "encryption": "aes-xts-plain64",
      "sector_size": 512
    }
  },
  "digests": {
    "0": {
      "type": "pbkdf2",
      "keyslots": [
        "0"
      ],
      "segments": [
        "0",
        "1"
      ],
...and so on and so forth...

結果:

# cryptsetup luksDump barfoo.img
Data segments:
  0: crypt
    offset: 121634816 [bytes]
    length: 88080384 [bytes]
    cipher: aes-xts-plain64
    sector: 512 [bytes]

  1: crypt
    offset: 16777216 [bytes]
    length: 104857600 [bytes]
    cipher: aes-xts-plain65
    sector: 512 [bytes]

残念ながら、この時点では、デバイスは正常に開き、サイズも正しく増加していました (やった!) が、データは間違っていました (いやだ!)。論理バイト 0 は、予想された 116MiB ではなく、物理バイト 16MiB にマップされていました。

おい、それはセグメント 0 ではなくセグメント 1 だよ、バカなビリー!何が悪かったんだ?

わかりません。まるで、まだ誰もテストしたことのない隠された機能をアンロックするために、単純なメタデータの変更を行うことは許可されていないかのようです。

私の理解不足によるものかもしれませんが、セグメントを逆の順序で配置しても、間違った順序で開き続けます。何かがこれらを物理オフセットで並べ替えているのでしょうか? しかし、なぜでしょうか?

残念ながら、この魔法は失敗に終わりました。私が何か間違ったことをしたか、まだ使用できる状態ではないからです。それでも、機能は一応あるので、将来にはまだ希望があります。

たとえそれが機能したとしても、それを使用することはお勧めできません。

関連情報