Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ else
MUSL_PATH_ARCH := $(ARCH)
endif

# Auto-detect the latest built fde-guest RPM
CRYPTPILOT_FDE_RPM := $(shell ls -t /root/rpmbuild/RPMS/$(ARCH)/cryptpilot-fde-guest-*.rpm 2>/dev/null | head -n 1)

.PHONE: help
help:
@echo "Read README.md first"
Expand Down
648 changes: 420 additions & 228 deletions cryptpilot-convert.sh

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions cryptpilot-core/src/fs/nbd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ impl NbdDevice {
let nbd_dev_num = Self::get_avaliable().await?;
let nbd_dev_path = nbd_dev_num.to_path();

tracing::info!("Connecting disk image {disk_img:?} to NBD device {nbd_dev_path:?}");

// The problem is that the nbd device may be use by the kernel (e.g. as mount point or as a device mapper) due to the annoying udev rules. Here we try to add a udev rule to ingore this device.
let udev_rule = UdevRule::install_ignore_nbd_rule().await?;

Expand All @@ -89,6 +91,10 @@ impl NbdDevice {
tracing::debug!("Waiting 1 second for the nbd device to be ready");
tokio::time::sleep(std::time::Duration::from_secs(1)).await;

// Ensure partition device nodes are created before proceeding
let _ = Command::new("partprobe").arg(&nbd_dev_path).run().await;
tokio::time::sleep(std::time::Duration::from_millis(500)).await;

let device = Self {
nbd_dev_num,
udev_rule,
Expand Down
72 changes: 9 additions & 63 deletions cryptpilot-fde/docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ This guide walks you through encrypting a bootable OS disk with full disk encryp
## Prerequisites

- cryptpilot-fde-host installed on your system
- A bootable qcow2 disk image, or an unmounted real disk
- A bootable qcow2 disk image

> **Note**: The `--device` option for in-place disk encryption was removed since v0.7.0.
> Use `--in`/`--out` for file-based conversion only.

## Prepare Configuration

Expand Down Expand Up @@ -289,59 +292,7 @@ Example output:
}
```

## Example 5: Encrypt a Real System Disk

For production systems, you need to encrypt a real disk.

> [!IMPORTANT]
> **DO NOT encrypt the active disk you are booting from!**
>
> You must:
> 1. Unbind the disk from the instance
> 2. Bind it to another instance as a data disk
> 3. Encrypt it
> 4. Re-bind it to the original instance

### Steps

1. **Prepare configuration** (same as above):

```sh
mkdir -p ./config_dir
cat << EOF > ./config_dir/fde.toml
[rootfs]
delta_location = "disk"

[rootfs.encrypt.exec]
command = "echo"
args = ["-n", "AAAaaawewe222"]

[delta]
integrity = true

[delta.encrypt.exec]
command = "echo"
args = ["-n", "AAAaaawewe222"]
EOF
```

2. **Validate configuration**:

```sh
cryptpilot-fde-host -c ./config_dir/ config check --keep-checking
```

3. **Encrypt the disk** (assuming the disk is `/dev/nvme2n1`):

```sh
cryptpilot-convert --device /dev/nvme2n1 \
-c ./config_dir/ \
--rootfs-passphrase AAAaaawewe222
```

4. **Re-bind the disk** to the original instance and boot from it.

## Example 6: Using KBS Provider (Production)
## Example 4: Using KBS Provider (Production)

For production environments, use Key Broker Service with remote attestation.

Expand Down Expand Up @@ -372,10 +323,6 @@ EOF
# For disk images
cryptpilot-convert --in ./original.qcow2 --out ./encrypted.qcow2 \
-c ./config_dir/ --rootfs-passphrase <actual-rootfs-key>

# For real disks
cryptpilot-convert --device /dev/nvme2n1 \
-c ./config_dir/ --rootfs-passphrase <actual-rootfs-key>
```

### Boot Process
Expand All @@ -388,7 +335,7 @@ When booting, the system will:
4. If verified, KBS returns the decryption key
5. System decrypts and boots

## Example 7: Using KMS Provider (Cloud-Managed)
## Example 5: Using KMS Provider (Cloud-Managed)

For Alibaba Cloud users, use KMS for centralized key management.

Expand Down Expand Up @@ -485,11 +432,10 @@ Common issues:

If `cryptpilot-convert` fails:

1. **Check disk format**: Only qcow2 images are supported for disk images
1. **Check disk format**: qcow2 and VHD images are supported
2. **Check disk size**: Ensure enough space for encryption overhead
3. **For real disks**: Ensure the disk is unmounted and not in use
4. **Device already exists error**: If you see errors like `/dev/cryptpilot: already exists in filesystem`, it may be leftover from a previous failed convert. Try `dmsetup remove_all` to clean up
5. **Check logs**: The last convert's detailed log is saved at `/tmp/.cryptpilot-convert.log`
3. **Device already exists error**: If you see errors like `/dev/cryptpilot: already exists in filesystem`, it may be leftover from a previous failed convert. Try `dmsetup remove_all` to clean up
4. **Check logs**: The last convert's detailed log is saved at `/tmp/.cryptpilot-convert.log`

### Boot Failed

Expand Down
72 changes: 9 additions & 63 deletions cryptpilot-fde/docs/quick-start_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
## 前置条件

- 已安装 cryptpilot-fde
- 可启动的 qcow2 磁盘镜像,或未挂载的真实磁盘
- 可启动的 qcow2 磁盘镜像

> **注意**:自 v0.7.0 起,`--device` 选项(就地磁盘加密)已被移除。
> 请改用 `--in`/`--out` 进行基于文件的转换。

## 准备配置

Expand Down Expand Up @@ -289,59 +292,7 @@ cryptpilot-fde-host show-reference-value --disk ./uki-encrypted.qcow2
}
```

## 示例 5:加密真实系统磁盘

对于生产系统,你需要加密真实磁盘。

> [!IMPORTANT]
> **不要加密正在启动的活动磁盘!**
>
> 你必须:
> 1. 从实例解绑磁盘
> 2. 将其作为数据盘绑定到另一个实例
> 3. 加密它
> 4. 重新绑定到原始实例

### 步骤

1. **准备配置**(与上面相同):

```sh
mkdir -p ./config_dir
cat << EOF > ./config_dir/fde.toml
[rootfs]
delta_location = "disk"

[rootfs.encrypt.exec]
command = "echo"
args = ["-n", "AAAaaawewe222"]

[delta]
integrity = true

[delta.encrypt.exec]
command = "echo"
args = ["-n", "AAAaaawewe222"]
EOF
```

2. **验证配置**:

```sh
cryptpilot-fde-host -c ./config_dir/ config check --keep-checking
```

3. **加密磁盘**(假设磁盘是 `/dev/nvme2n1`):

```sh
cryptpilot-convert --device /dev/nvme2n1 \
-c ./config_dir/ \
--rootfs-passphrase AAAaaawewe222
```

4. **重新绑定磁盘**到原始实例并从其启动。

## 示例 6:使用 KBS 提供者(生产环境)
## 示例 4:使用 KBS 提供者(生产环境)

对于生产环境,使用带有远程证明的密钥代理服务。

Expand Down Expand Up @@ -372,10 +323,6 @@ EOF
# 磁盘镜像
cryptpilot-convert --in ./original.qcow2 --out ./encrypted.qcow2 \
-c ./config_dir/ --rootfs-passphrase <实际-rootfs-密钥>

# 真实磁盘
cryptpilot-convert --device /dev/nvme2n1 \
-c ./config_dir/ --rootfs-passphrase <实际-rootfs-密钥>
```

### 启动过程
Expand All @@ -388,7 +335,7 @@ cryptpilot-convert --device /dev/nvme2n1 \
4. 如果验证通过,KBS 返回解密密钥
5. 系统解密并启动

## 示例 7:使用 KMS 提供者(云托管)
## 示例 5:使用 KMS 提供者(云托管)

对于阿里云用户,使用 KMS 进行集中式密钥管理。

Expand Down Expand Up @@ -485,11 +432,10 @@ cryptpilot-fde-host -c ./config_dir/ config check --keep-checking

如果 `cryptpilot-convert` 失败:

1. **检查磁盘格式**:磁盘镜像仅支持 qcow2 格式
1. **检查磁盘格式**:支持 qcow2 和 VHD 格式
2. **检查磁盘大小**:确保有足够空间用于加密开销
3. **对于真实磁盘**:确保磁盘未挂载且不在使用中
4. **设备已存在错误**:如果出现类似 `/dev/cryptpilot: already exists in filesystem` 的错误,可能是上次 convert 失败遗留的,尝试 `dmsetup remove_all` 清除
5. **查看日志**:最后一次 convert 的详细日志保存在 `/tmp/.cryptpilot-convert.log`
3. **设备已存在错误**:如果出现类似 `/dev/cryptpilot: already exists in filesystem` 的错误,可能是上次 convert 失败遗留的,尝试 `dmsetup remove_all` 清除
4. **查看日志**:最后一次 convert 的详细日志保存在 `/tmp/.cryptpilot-convert.log`

### 启动失败

Expand Down
20 changes: 17 additions & 3 deletions cryptpilot-fde/src/disk/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,18 +262,32 @@ impl OnExternalFdeDisk {
// Obtain all partitions under the device
let lsblk_stdout = {
let mut cmd = Command::new("lsblk");
cmd.args(["-lnpo", "NAME"]);
cmd.args(["-lnpo", "NAME,TYPE,FSTYPE"]);
cmd.arg(hint_device);
cmd.run().await.context("Failed to list partitions")?
};

let lsblk_str = String::from_utf8(lsblk_stdout)?;
// Filter for partition lines (first field NAME ends with a digit)
let candidate_partitions = lsblk_str
.lines()
.filter(|line| line.chars().last().map(|c| c.is_numeric()).unwrap_or(false))
.map(PathBuf::from)
.filter_map(|line| {
let fields: Vec<&str> = line.split_whitespace().collect();
if fields.is_empty() {
return None;
}
let name = fields[0];
let last_char = name.chars().last()?;
if last_char.is_numeric() {
Some(PathBuf::from(name))
} else {
None
}
})
.collect::<Vec<_>>();

tracing::debug!("Candidate EFI partitions: {:?}", candidate_partitions);

for part in candidate_partitions {
let is_efi_part = async {
// Create a temporary mount point
Expand Down
Loading