dm-integrity 가 결합된 dm-crypt 파티션에서 데이터 복구하기
증상
- 블록 디바이스
/dev/nvme0n1p4
에 리눅스 dm-crypt 를 Authenticated disk encryption 를 포함하여 활성화한 후, 그 위에 XFS 파일시스템을 포맷하여 사용하였는데,cryptsetup luksFormat
을 이용하여 LUKS 파티션을 만들 때--integirty
옵션을 주면 됩니다.- 아울러 성능 문제로
cryptsetup luksOpen
명령어에--integrity-no-journal
옵션을 켜 둔 상태였습니다.
- 갑작스러운 전원 문제로 시스템이 다운된 후, 재부팅하였을 때 아래 커널 메시지를 내며 XFS 파일시스템이 마운트되지 않는 상황입니다.
[52276.211696] XFS: attr2 mount option is deprecated.
[52276.226177] XFS (dm-4): Mounting V5 filesystem in no-recovery mode. Filesystem will be inconsistent.
[52276.236100] trusted_key: device-mapper: crypt: dm-3: INTEGRITY AEAD ERROR, sector 2
[52276.243831] XFS (dm-4): metadata I/O error in "xfs_read_agi+0xa0/0x144" at daddr 0x2 len 1 error 84
[52276.252987] XFS (dm-4): xfs_imap_lookup: xfs_ialloc_read_agi() returned error -84, agno 0
[52276.261234] XFS (dm-4): Failed to read root inode 0x80, error 84
이는 dm-integrity 저널이 꺼진 상태에서 시스템이 예상치 않게 종료되어, integrity tag가 데이터와 맞지 않게 되어 생긴 문제 입니다.
이러한 상황에서 dm-integrity를 걷어내고 파일시스템을 마운트하는 방법을 보겠습니다.
Cipher와 키의 길이를 확인하기
cryptsetup luksDump /dev/mapper/nvme0n1p4
명령어을 이용하여 암호화 키의 길이를 확인합니다. 그 결과의 일부만 보면,
Keyslots:
0: luks2
Key: 768 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
Cipher는 aes-xts-plain64
이며 Cipher key 길이는 512비트 (64바이트) 입니다. 전체 키의 길이는 768비트 (96바이트) 인데, 이 중 앞의 64 바이트는 Cipher key이고 나머지는 dm-integrity를 통한 Authenticated Encryption with Additional Data (AEAD) 를 위한 키이며, 나중에 이를 걷어내게 될 것입니다.
cryptstup luksOpen 을 통해 키 획득하기
평상시와 같은 cryptseupt luksOpen
을 이용하여 LUKS 컨테이너를 열되, --disable-keyring
옵션을 추가합니다.
cryptsetup --disable-keyring --integrity-no-journal luksOpen /dev/nvme0n1p4 PV10SN1D
그 이후 lsblk
를 사용하면 그 상태가 아래와 같을 것입니다.
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
nvme0n1 259:0 0 931.5G 0 disk
└─nvme0n1p4 259:4 0 723.1G 0 part
└─PV10SN1D_dif 253:3 0 680.5G 0 crypt
└─PV10SN1D 253:4 0 680.5G 0 crypt
그 이후 dmsetup table --showkeys
를 하면 암호화 키를 획득할 수 있습니다.
dmsetup table --showkeys
를 통해 노출된 키가 셸 히스토리, 스크린샷 등에 남지 않도록 주의하십시오.PV10SN1D: 0 1427017544 crypt capi:authenc(hmac(sha256),xts(aes))-plain64 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy 0 253:3 0 1 integrity:32:aead
PV10SN1D_dif: 0 1427017544 integrity 259:4 32768 32 D 4 journal_sectors:131032 interleave_sectors:32768 buffer_sectors:128 fix_padding
위에서 x또는 y로 가린 부분이 실제 데이터로 채워져 있어야 합니다.
- 만약 0으로 채워져 있다면,
dmsetup table
명령어에--showkeys
옵션을 사용하였는지 확인합니다. - 만약
:96:logon:cryptsetup:a895642f-432e-48be-a370-276a801c85e3-d0
와 같이 표시되어 있다면,cryptsetup luksOpen
에--disable-keyring
옵션을 주었는지 확인합니다.
이제 cryptsetup close
를 통해 열었던 LUKS 컨테이너를 도로 닫아줍니다.
Authenticated disk encryption 걷어내기
위에서 dmsetup table --showkeys
를 통해 획득하셨던 결과물에 아래의 수정을 가합니다.
- dm-integirty target에 대하여 (
PV10SN1D_dif
)- 대문자
D
만 있는 부분을R
로 고칩니다.- 만약 위의
cryptsetup luksOpen
에--integrity-no-journal
옵션을 사용하지 않았더라면, 대문자D
가 아닌J
였을 수도 있겠습니다. 아무튼R
로 고칩니다. - 이 옵션의 의미에 대하여는 여기에서 확인할 수 있습니다. R은 dm-integrity의 복구 모드(recovery mode)를 의미합니다.
- 만약 위의
- 대문자
- dm-crypt target에 대하여 (
PV10SN1D
)capi:authenc(hmac(sha256),xts(aes))-plain64
를aes-xts-plain64
로 고칩니다.- 이는 위의
cryptsetup luksDump /dev/mapper/nvme0n1p4
를 통하여 확인한 값입니다.
- 이는 위의
- 바로 뒤에 따르는 키가 ᅟ192자인데, 16진법의 표현에서 2글자당 1바이트이므로 총 96바이트인데, 앞의 64바이트만 남기고 나머지는 지웁니다.
- 다시 말하자면 32바이트를 지워야 하는데, 2글자당 1바이트이므로 64글자를 지웁니다.
- 이 게시물에서 보여드리고 있는 예제에서는, x로 표시한 부분만 남기고 y로 표시한 부분은 지운다는 의미입니다.
- 여기서 64타이트는
cryptsetup luksDump /dev/mapper/nvme0n1p4
를 통하여 확인한 cipher key 값입니다.
- 이 문서를 참고하여
integrity:32:aead
옵션을 지우고,#opt_params
에 해당하는 숫자를 1만큼 내립니다.- 이 예제에서는, 가장 마지막에 있는
1 integirty:32:aead
가#opt_params
와opt_params
인데,opt_params
에서integrity:32:aead
를 지우고#opt_params
에 해당하는 1은 0으로 내립니다.
- 이 예제에서는, 가장 마지막에 있는
각 행의 맨 앞에 붙는 PV10SN1D:
와 PV10SN1D_dif:
를 지우고, 각 옵션을 integrity_target
과 crypt_target
으로 저장하면 아래와 같습니다.
0 1427017544 integrity 259:4 32768 32 R 4 journal_sectors:131032 interleave_sectors:32768 buffer_sectors:128 fix_padding
0 1427017544 crypt aes-xts-plain64 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0 253:3 0
이 데이터의 규격에 대해서 좀 더 자세히 알아보고 싶으시다면, 이 문서의 "TABLE FORMAT" 단락을 참고하시면 되겠습니다.
dmsetup 을 이용하여 dm-integrity와 dm-crypt를 설정하고, XFS 마운트하기
dmsetup create PV10SN1D_dif < ./integrity_target
dmsetup create PV10SN1D < ./crypt_target
이후 lsblk
를 이용하여 정상적으로 target이 생성된 것을 확인한 후,
mount -oro,norecovery /dev/mapper/PV10SN1D /mnt
를 이용하면 마운트가 정상적으로 이루어지고, 데이터를 확인할 수 있게 됩니다. 이 때의 커널 로그는 아래와 같습니다.
[55980.242565] XFS (dm-4): Mounting V5 filesystem in no-recovery mode. Filesystem will be inconsistent.
마운트 해제 이후 dmsetup remove PV10SN1D
및 dmsetup remove PV10SN1D_dif
를 하면 정리됩니다.
R
)을 이용하였으므로, dm-integrity 의 무결성 검사가 완전히 비활성화되며 장치에 쓰기 작업이 허용되지 않습니다. 파일시스템 마운트 후 읽을 수 있는 데이터를 안전한 저장소로 복사하여야 합니다.또한
norecovery
는 파일시스템 자체의 복구 능력을 사용할 수 없는 설정입니다. 파일시스템 수준의 불일치(inconsistency)가 보일 수 있습니다. 현 단계에서 최대한의 데이터를 안전한 곳으로 저장한 후, 아래 단락을 참고하여 추가적인 복구를 시도하면 좋습니다.norecovery 를 사용하지 않고 파일시스템을 마운트하고 싶다면
파일시스템 (이 경우는 XFS) 가 가진 자체적인 복구 능력을 이용하고자 한다면 norecovery
옵션을 마운트 때 사용해서는 안되는데, dm-integrity 에 복구 모드(R
)가 켜져있다면 블록 디바이스에 쓰기 작업이 허용되지 않으므로 ro
및 norecovery
옵션이 필수로 요구됩니다.
dm-integrity 의 복구 모드가 켜진 상태에서 마운트에 성공했다면, 살릴 수 있는 데이터를 최대한 확보한 후, dm-integrity 옵션을 R
이 아닌 원래의 것 (가령 J
나 D
)로 설정한 후, ro
및 norecovery
옵션을 제외하여 파일시스템 마운트를 시도해봅니다. 이 작업이 성공적이었다면 커널 로그는 아래와 같습니다.
[56413.859119] XFS (dm-4): Mounting V5 Filesystem
[56413.951776] XFS (dm-4): Starting recovery (logdev: internal)
[56413.980213] XFS (dm-4): Ending recovery (logdev: internal)