kWatanabe 記事一覧へ

kWatanabe の 技術帖

某企業でOSや仮想化の研究をやっているインフラ系エンジニア。オンプレとクラウドのコラボレーションなど、興味ある技術を綴る。

修理交換に備えて、LinuxサーバのHDDを暗号化する

本記事は暗号化やセキュリティに関する記述がありますが、個人のメモ書きであり、内容の正しさを含め、直接的・間接的を問わず生じた問題等に対して、著者は一切の責任を負いません。

概要

  • 保証期間内にHDDが故障した際、修理交換のためにHDDを送付したいが、中のデータを削除できずに送付できないジレンマがある
  • HDDが故障する前に透過的暗号を導入しておくことで、HDDからのデータ流出を防止する

自宅サーバのHDDが壊れた

我が家のサーバは、NASエンタープライズ向けのHDDを利用しており、保証期間が数年と長く設定されているが、今回は残念ながら期限が切れていた。無念。

一方、保証期間内であったとしても、データを含むHDDをメーカーに送ることは中々難しい。

故障品だとしても、検査の際にたまたま動くこともあるだろうし、メーカーならば故障部品を交換すればサルベージも簡単だと考えられる。廃棄時のように物理破壊するわけにもいかないし、故障しているので上書き消去もできない。

透過的暗号による保護

このような背景から、故障する前に他人からデータを読み取れない状態にしたい。その一方で、故障するまでは特別な操作なしに扱えるようにしたい。

これを実現するために透過的暗号を導入する。透過的暗号は、アクセスの際に自動で暗号と復号を行う仕組みで、鍵を持つ者は通常のHDDと同様に扱えるが、鍵を持たない者はデータが読み取れない。故障時はHDDのみを提出することで、物理アクセスによる情報流出の防止に期待する。

dm-crypt + LUKS

Linux の透過的暗号の仕組みとしては、dm-crypt と LUKS の組み合わせが主流らしい。

gitlab.com

ディスク単体の暗号化から、システム全体の暗号化、複数の鍵を用いたアクセス制御や、ネットワークを用いた鍵配信まで、かなり高度な構成が取れる模様。

想定する驚異モデル

設定を検討するうえで、故障したHDDをメーカー修理に出すユースケースを想定し、驚異モデルを以下の通りとする。

  • 攻撃者は、機密情報が含まれるHDD本体を入手できる
  • 攻撃者は、入手したHDD本体へのあらゆる物理的アクセスができる
  • 攻撃者は、HDDが搭載されていたサーバやその他のデバイスにはアクセスできない
  • 内部犯・外部犯による攻撃や盗難は考慮しない。

すなわち、以下の設定では情報システムとしてのセキュリティ対策にはならない点に注意。

検証

  • Debian 12.4 (bookwarm) amd64
  • cryptsetup 2.6.1-4~deb12u1

パッケージの導入とアルゴリズムの選定

基本的な機能はカーネルが提供するため、導入するパッケージは管理ツールのみ。

$ sudo apt install cryptsetup

暗号化に用いるアルゴリズムを選定するため、ベンチマークを行う。

$ sudo cryptsetup benchmark
# テストはストレージI/Oがなくメモリ上のもののため目安です。
PBKDF2-sha1      2396745 回/秒 (256 ビットの鍵)
PBKDF2-sha256    4319571 回/秒 (256 ビットの鍵)
PBKDF2-sha512    1829975 回/秒 (256 ビットの鍵)
PBKDF2-ripemd160 1071068 回/秒 (256 ビットの鍵)
PBKDF2-whirlpool  799219 回/秒 (256 ビットの鍵)
argon2i       6 回, 1048576 KB使用, 4 スレッド (256 のビットの鍵) (2000 ms 計測)
argon2id      6 回, 1048576 KB使用, 4 スレッド (256 のビットの鍵) (2000 ms 計測)
#     Algorithm |      キー |          暗号化 |           復号化
        aes-cbc        128b      1768.2 MiB/s      6683.9 MiB/s
    serpent-cbc        128b       118.3 MiB/s       816.0 MiB/s
    twofish-cbc        128b       261.4 MiB/s       526.6 MiB/s
        aes-cbc        256b      1415.0 MiB/s      5387.8 MiB/s
    serpent-cbc        256b       125.2 MiB/s       815.0 MiB/s
    twofish-cbc        256b       297.4 MiB/s       527.2 MiB/s
        aes-xts        256b      5489.9 MiB/s      5485.2 MiB/s
    serpent-xts        256b       779.2 MiB/s       740.5 MiB/s
    twofish-xts        256b       488.7 MiB/s       498.9 MiB/s
        aes-xts        512b      4897.4 MiB/s      4866.2 MiB/s
    serpent-xts        512b       786.7 MiB/s       740.2 MiB/s
    twofish-xts        512b       492.0 MiB/s       499.0 MiB/s

このシステムでは、ハッシュ化アルゴリズムsha256、暗号化アルゴリズムaes-xts が最も高速であることが分かる*1。256bitのAESならば強度の面でも申し分ない*2

暗号化ボリュームの作成

今回は、/dev/sdbパーティションを作成し、暗号化ボリュームとする。パーティションの作成に特別な手順は必要ない。

$ sudo fdisk /dev/sdb
Command (m for help): g
Command (m for help): n
Partition number (1-128, default 1): 
First sector (2048-16777182, default 2048): 
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-16777182, default 16775167): 
Command (m for help): w

ベンチマークの結果を参考に、暗号化ボリュームを初期化する。今回は、(故障するかもしれない)HDD以外にはアクセスされない前提のため、アンロックするための鍵は、別のディスクにキーファイルを作成し、これを用いる。

まず、キーファイルを作成する。

$ sudo mkdir -p /etc/luks-keyfile
$ cat /dev/urandom | tr -dc "[:graph:]" | fold -w 2048| head -n 1 | sudo tee /etc/luks-keyfile/test
...

一応、root 以外で閲覧できないようパーミッションを設定する。

$ sudo chmod 700 /etc/luks-keyfile
$ sudo chmod 400 /etc/luks-keyfile/test

暗号化ボリュームを作成する。

$ sudo cryptsetup -v --cipher aes-xts-plain64 --key-size 256 --hash sha256 luksFormat /dev/sdb1 /etc/luks-keyfile/test

警告!!
========
/dev/sdb1 のデータを上書きします。戻せません。

よろしいですか? ('yes' を大文字で入力してください): YES
キースロット 0 が作成されました。
コマンド成功。

手動でのアンロック

作成した暗号化ボリュームをアンロックし、 /dev/mapper/crypt-sdb1 としてマップする。

$ sudo cryptsetup open --type luks --key-file /etc/luks-keyfile/test /dev/sdb1 crypt-sdb1

すると、以下のように透過的暗号のレイヤが挿入される。

$ sudo lsblk -p /dev/sdb
NAME                       MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
/dev/sdb                     8:16   0    8G  0 disk  
└─/dev/sdb1                  8:17   0    8G  0 part  
  └─/dev/mapper/crypt-sdb1 254:0    0    8G  0 crypt 

以降は、このレイヤを物理HDDと同じように扱うことで、透過的暗号が実現される。

$ sudo mkfs -t ext4 /dev/mapper/crypt-sdb1
$ sudo mount /dev/mapper/crypt-sdb1 /mnt
$ ls /mnt
lost+found/

自動でのアンロック

今回は、利用中の盗難や攻撃を想定しないため、システムが起動すると自動でアンロックされ、ファイルシステムとしてマウントできるようにする。

そのために、まず暗号化ボリュームのUUIDを調べる。

$ sudo blkid /dev/sdb1
/dev/sdb1: UUID="e8e54b6a-6312-456b-bdff-5c7762b69d01" TYPE="crypto_LUKS" PARTUUID="30a698c2-c227-7f4c-8239-164128126283"

調べたUUID、マップするデバイスファイル、アンロックのためのキーファイルを /etc/crypttab に記述する。

$ sudo vi /etc/crypttab
crypt-sdb1 UUID=e8e54b6a-6312-456b-bdff-5c7762b69d01 /etc/luks-keyfile/test luks,timeout=30

これで、起動時に自動でアンロックされるため、通常のHDDと同様に /etc/fstab にマウントポイントを記述しておくと、アンロックとマウントまで自動で行われる。

$ sudo vi /etc/fstab
/dev/mapper/crypt-sdb1 /srv ext4 defaults 0 0
$ sudo reboot
...
$ df /dev/mapper/crypt-sdb1
ファイルシス           1K-ブロック    使用   使用可 使用% マウント位置
/dev/mapper/crypt-sdb1     8136484      24  7701568    1% /srv

ソフトウェアRAIDの透過的暗号

ソフトウェアRAIDと透過的暗号を組み合わせる場合、以下の2パターンがある。

  • 暗号化ボリュームを使って、RAIDボリュームを作成 暗号化ボリュームを従来の物理HDDと同様に扱えるため、RAIDボリュームの管理や縮退時の対応が楽。ただし、HDDごとに暗号化ボリュームの構成が必要。
  • RAIDボリュームを使って、暗号化ボリュームを作成 全てのHDDをひとつの暗号化ボリュームでまとめることができるが、RAIDボリュームの管理や縮退時の対応が難しい。

特に理由がなければ、前者が扱いやすい。この場合、特別な手順は必要なく、暗号化ボリュームに対して普通にRAIDの設定を行えば良い。

$ sudo lsblk -p /dev/sdc /dev/sdd      
NAME                        MAJ:MIN RM SIZE RO TYPE  MOUNTPOINTS
/dev/sdc                      8:32   0   8G  0 disk  
└─/dev/sdc1                   8:33   0   8G  0 part  
  └─/dev/mapper/crypt-raid0 254:0    0   8G  0 crypt 
/dev/sdd                      8:48   0   8G  0 disk  
└─/dev/sdd1                   8:49   0   8G  0 part  
  └─/dev/mapper/crypt-raid1 254:1    0   8G  0 crypt
$ sudo apt install mdadm
$ sudo mdadm --create --level=1 --metadata=1.2 --raid-devices=2 /dev/md0 /dev/mapper/crypt-raid0 /dev/mapper/crypt-raid1
mdadm: array /dev/md0 started.
$ sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
ARRAY /dev/md0 metadata=1.2 name=dmcrypt:0 UUID=07997157:3fd8cdc2:9fcf07a3:ce952c0f
$ sudo mdadm --assemble --scan
$ sudo lsblk -p /dev/sdc /dev/sdd  
NAME                        MAJ:MIN RM SIZE RO TYPE  MOUNTPOINTS
/dev/sdc                      8:32   0   8G  0 disk  
└─/dev/sdc1                   8:33   0   8G  0 part  
  └─/dev/mapper/crypt-raid0 254:0    0   8G  0 crypt 
    └─/dev/md0                9:0    0   8G  0 raid1 
/dev/sdd                      8:48   0   8G  0 disk  
└─/dev/sdd1                   8:49   0   8G  0 part  
  └─/dev/mapper/crypt-raid1 254:1    0   8G  0 crypt 
    └─/dev/md0                9:0    0   8G  0 raid1
$ sudo mkfs -t ext4 /dev/md0 
...
$ sudo blkid /dev/md0
/dev/md0: UUID="b9a593ff-3ea4-46aa-98fa-b93428a00fd1" BLOCK_SIZE="4096" TYPE="ext4"
$ sudo vi /etc/fstab
UUID=b9a593ff-3ea4-46aa-98fa-b93428a00fd1 /opt ext4 defaults 0 0
$ sudo mount -a
$ sudo lsblk -p /dev/sdc /dev/sdd
NAME                        MAJ:MIN RM SIZE RO TYPE  MOUNTPOINTS
/dev/sdc                      8:32   0   8G  0 disk  
└─/dev/sdc1                   8:33   0   8G  0 part  
  └─/dev/mapper/crypt-raid0 254:0    0   8G  0 crypt 
    └─/dev/md0                9:0    0   8G  0 raid1 /opt
/dev/sdd                      8:48   0   8G  0 disk  
└─/dev/sdd1                   8:49   0   8G  0 part  
  └─/dev/mapper/crypt-raid1 254:1    0   8G  0 crypt 
    └─/dev/md0                9:0    0   8G  0 raid1 /opt

まとめ

  • 保証期間内に壊れたHDDを修理に出せるようにするため、dm-crypt と LUKS を導入した
  • セキュリティ対策としては機能しないが、HDDのみを提出する必要のあるケースにおいては、最低限の対策となると思う

*1:aes系がやたら早いのは、このシステムが搭載しているCPUがAES-NIをサポートしているため。

*2:個人の感想です。