Skip to content

Commit

Permalink
cdh/docs: add document for secure mount with block devices
Browse files Browse the repository at this point in the history
add document for secure mount with block devices.

Signed-off-by: ChengyuZhu6 <[email protected]>
  • Loading branch information
ChengyuZhu6 authored and Xynnn007 committed Jul 22, 2024
1 parent c231a16 commit a43a325
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 0 deletions.
25 changes: 25 additions & 0 deletions confidential-data-hub/docs/SECURE_STORAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,28 @@ flowchart LR
```

For more details, please refer to [the guide](use-cases/secure-mount-with-aliyun-oss.md).

### Block Device

The [plugin](../storage/src/volume_type/blockdevice) provides ways to encrypt a block device and mount it to a specific mount point. Currently only support LUKS in [cryptsetup](https://gitlab.com/cryptsetup/cryptsetup/) for block device encryption.

#### LUKS Encryption

In this mode, the device would be encrypted as LUKS device first, and then mount it to a target path to store the data to protect the confidentiality and integrity of the data.

The architecture diagram is

```mermaid
flowchart LR
A[Local/Network] -- mount --> B[Block Device]
subgraph TEE Guest
B -- Check if encrypted --> F{Is Encrypted?}
F -- No --> G[Encrypt by cryptsetup]
G -- encrypt --> C[LUKS Encrypted Block Device]
F -- Yes --> C[LUKS Encrypted Block Device]
C -- open and mapping --> D[Mapped Device]
D -- mount --> E[Target Path]
end
```

For more details, please refer to [the guide](use-cases/secure-mount-with-block-device.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Secure mount with Block Device

This guide helps an user to use confidential data hub to secure mount inside TEE environment.

## Preliminaries

- Ensure that [cryptsetup](https://gitlab.com/cryptsetup/cryptsetup/) is installed.

## Example

### Create a loop device

```shell
$ loop_file="/tmp/test.img"
$ sudo dd if=/dev/zero of=$loop_file bs=1M count=1000
$ sudo losetup -fP $loop_file
$ device=$(sudo losetup -j $loop_file | awk -F'[: ]' '{print $1}')
$ echo $device
# Output should be something like /dev/loop0
$ device_num=$(sudo lsblk -no MAJ:MIN $device)
$ echo $device_num
# Output should be something like 7:0
```

### Secure mount inside a TEE environment

1. Build the CDH and its client tool

Follow the instructions in the [CDH README](../../README.md#confidential-data-hub) and [Client Tool README](../../README.md#client-tool) to build the CDH and its client tool.

2. Install `luks-encrypt-storage`

Install [luks-encrypt-storage](../../storage/scripts/luks-encrypt-storage) into `/usr/local/bin`

3. Run CDH
```shell
$ confidential-data-hub
```

4. Prepare a request JSON `storage.json`
```json
{
{
"volume_type": "BlockDevice",
"options": {
"deviceId": "7:0",
"encryptType": "LUKS",
"dataIntegrity": "true"
},
"flags": [],
"mount_point": "/mnt/test-path"
}
}

```
- Fields:
- `volume_type`: The secure mount plugin type name. It determines how the rest of the fields are used.
- `options`: A key-value map specifying the settings for the mount operation. Different plugins can define different keys in the options. In this example, all keys are for block devices.
- `flags`: A string list specifying settings for the mount operation. Different plugins can define different uses for this field.
- `mount_point`: The target mount path for the operation.

- Options Fields:
- `deviceId`: The device number, formatted as "MAJ:MIN".
- `encryptType`: The encryption type. Currently, only LUKS is supported.
- `encryptKey`: Encryption key. It can be a sealed secret or a resource uri. If not set, it means that the device is unencrypted and a random 4096-byte key will be generated to encrypt the device.
- `dataIntegrity`: Enables dm-integrity to protect data integrity. Note that enabling data integrity will reduce IO performance by more than 30%.

5. Make a request to CDH
```shell
$ client-tool secure-mount --storage-path storage.json

# Check the target path to see if the mount succeeded
$ lsblk |grep "encrypted_disk"
# Expected output:
└─encrypted_disk_OEyEj_dif 253:1 0 968.6M 0 crypt
└─encrypted_disk_OEyEj 253:2 0 968.6M 0 crypt /mnt/test-path
```
149 changes: 149 additions & 0 deletions confidential-data-hub/storage/scripts/luks-encrypt-storage
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/bin/bash
#
# Copyright (c) 2022 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#

set -o errexit
set -o nounset
set -o pipefail
set -o errtrace

[ -n "${DEBUG:-}" ] && set -o xtrace

handle_error() {
local exit_code="${?}"
local line_number="${1:-}"
echo "error:"
echo "Failed at $line_number: ${BASH_COMMAND}"
exit "${exit_code}"
}
trap 'handle_error $LINENO' ERR

die()
{
local msg="$*"
echo >&2 "ERROR: $msg"
exit 1
}

setup()
{
local cmds=()

cmds+=("cryptsetup" "mkfs.ext4" "mount")

local cmd
for cmd in "${cmds[@]}"
do
command -v "$cmd" &>/dev/null || die "need command: '$cmd'"
done
}

setup

device_num=${1:-}
if [ -z "$device_num" ]; then
die "invalid arguments, at least one param for device num"
fi

is_encrypted="false"
if [ -n "${2-}" ]; then
is_encrypted="$2"
fi

mount_point="/tmp/target_path"
if [ -n "${3-}" ]; then
mount_point="$3"
fi

storage_key_path="/run/encrypt_storage.key"
if [ -n "${4-}" ]; then
storage_key_path="$4"
fi

data_integrity="true"
if [ -n "${5-}" ]; then
data_integrity="$5"
fi

device_name=$(sed -e 's/DEVNAME=//g;t;d' "/sys/dev/block/${device_num}/uevent")
device_path="/dev/$device_name"

opened_device_name=$(mktemp "encrypted_disk_XXXXX")

if [[ -n "$device_name" && -b "$device_path" ]]; then

if [ "$is_encrypted" == "false" ]; then

if [ "$data_integrity" == "false" ]; then
echo "YES" | cryptsetup luksFormat --type luks2 "$device_path" --sector-size 4096 \
--cipher aes-xts-plain64 "$storage_key_path"
else
# Wiping a device is a time consuming operation. To avoid a full wipe, integritysetup
# and crypt setup provide a --no-wipe option.
# However, an integrity device that is not wiped will have invalid checksums. Normally
# this should not be a problem since a page must first be written to before it can be read
# (otherwise the data would be arbitrary). The act of writing would populate the checksum
# for the page.
# However, tools like mkfs.ext4 read pages before they are written; sometimes the read
# of an unwritten page happens due to kernel buffering.
# See https://gitlab.com/cryptsetup/cryptsetup/-/issues/525 for explanation and fix.
# The way to propery format the non-wiped dm-integrity device is to figure out which pages
# mkfs.ext4 will write to and then to write to those pages before hand so that they will
# have valid integrity tags.
echo "YES" | cryptsetup luksFormat --type luks2 "$device_path" --sector-size 4096 \
--cipher aes-xts-plain64 --integrity hmac-sha256 "$storage_key_path" \
--integrity-no-wipe
fi
fi

cryptsetup luksOpen -d "$storage_key_path" "$device_path" $opened_device_name
rm "$storage_key_path"

if [ "$data_integrity" == "false" ]; then
mkfs.ext4 /dev/mapper/$opened_device_name -E lazy_journal_init
else
# mkfs.ext4 doesn't perform whole sector writes and this will cause checksum failures
# with an unwiped integrity device. Therefore, first perform a dry run.
output=$(mkfs.ext4 /dev/mapper/$opened_device_name -F -n)

# The above command will produce output like
# mke2fs 1.46.5 (30-Dec-2021)
# Creating filesystem with 268435456 4k blocks and 67108864 inodes
# Filesystem UUID: 4a5ff012-91c0-47d9-b4bb-8f83e830825f
# Superblock backups stored on blocks:
# 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
# 4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
# 102400000, 214990848
delimiter="Superblock backups stored on blocks:"
blocks_list=$([[ $output =~ $delimiter(.*) ]] && echo "${BASH_REMATCH[1]}")

# Find list of blocks
block_nums=$(echo "$blocks_list" | grep -Eo '[0-9]{4,}' | sort -n)

# Add zero to list of blocks
block_nums="0 $block_nums"

# Iterate through each block and write to it to ensure that it has valid checksum
for block_num in $block_nums
do
echo "Clearing page at $block_num"
# Zero out the page
dd if=/dev/zero bs=4k count=1 oflag=direct \
of=/dev/mapper/$opened_device_name seek="$block_num"
done

# Now perform the actual ext4 format. Use lazy_journal_init so that the journal is
# initialized on demand. This is safe for ephemeral storage since we don't expect
# ephemeral storage to survice a power cycle.
mkfs.ext4 /dev/mapper/$opened_device_name -E lazy_journal_init
fi

[ ! -d "$mount_point" ] && mkdir -p $mount_point

mount /dev/mapper/$opened_device_name $mount_point
else
die "Invalid device: '$device_path'"
fi

0 comments on commit a43a325

Please sign in to comment.