-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit: - Introduces the concept of the initrd SDK in support of disk image building - Adds a short README explaining the goal of the initrd SDK - Adds the first script to the SDK Signed-off-by: Adam Rozman <adam.rozman@est.tech>
- Loading branch information
Showing
2 changed files
with
185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Initrd SDK | ||
|
||
The Initrd SDK folder contains scripts and documentation to help integrate | ||
features e.g. LUKS and TPM support into the linux "initrd/initramfs" of disk | ||
images built as part of the Metal3 project. | ||
|
||
This directory is just a loose collection of scripts and information that | ||
can be injected at different stages of the image building and would be | ||
eventually executed during the boot process of a machine. | ||
|
||
## Initrd environment | ||
|
||
Usually in initrd/initramfs images are built with e.g. a tool like `dracut`. | ||
An initrd is required to be as small as possible so it usually lacks any kind | ||
of user space tooling. | ||
|
||
## unlock-mount-luks.sh | ||
|
||
This is a script that can be injected to initramfs images that were built with | ||
dracut and the script relies on only two external tools `blkid` and | ||
`systemd-cryptsetup`. If an image was built with `dracut` and the `dracut` | ||
module `crypt` is enabled then both `blkid` and `systemd-cryptsetup` should be | ||
present in the initrd environment. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
#!/bin/bash | ||
|
||
# This script is intedended to be used in a initrd/initramfs built by | ||
# dracut. The purpose of the script is to unlock and mount the root partition | ||
# and unlock the cloud-init config drive. | ||
# | ||
# The script works also without encryption but it doesn't support the | ||
# scenario when only the config drive is encrypted. | ||
# | ||
# The script has 1 mandatory and 3 optional positional arguments | ||
# Such as: | ||
# - path to the device file of the root partition (mandatory) | ||
# - path to the script that provides the encryption key in plain text format | ||
# - the partition number of the config-drive | ||
# - flag to run in dry run mode (nothig will get created/unlocked/mounted | ||
|
||
set -eu | ||
|
||
_is_luks() { | ||
# check blkid record of a device and determine if it is luks encrypted | ||
# arguments - any path to device file e.g. /dev/sd*, | ||
# /dev/disk/by-label/<your_label>,by-uuid/<your_id> etc.. | ||
local _record _half_type _full_type _device_path | ||
_device_path="$1" | ||
_record="$(blkid "${_device_path}")" | ||
_half_type="${_record##*TYPE=\"}" | ||
_full_type="${_half_type%%\"*}" | ||
if [[ "crypto_LUKS" == "${_full_type}" ]]; then | ||
return 0 | ||
fi | ||
return 1 | ||
} | ||
|
||
_get_partition_from_blkid() { | ||
# remove all but the device name of the blkid record | ||
# arguments - any path to deevice file e.g. /dev/sd*, | ||
# /dev/disk/by-label/<yourlabel>,by-uuid/<your_id> etc.. | ||
local _record _partition_device_path _partition _uuid _blkid | ||
_partition_device_path="$1" | ||
_record="$(blkid "${_partition_device_path}")" | ||
# take the UUID then, run regular blkid w/o arguments and match the UUID | ||
#_record="${_record##*PARTUUID=}" | ||
#_uuid="${_record%% *}" | ||
_uuid="${_record##*PARTUUID=}" | ||
|
||
blkid | while read -r _pname _ _ _iter_uuid; do | ||
if [[ "${_uuid}" == "${_iter_uuid##*PARTUUID=}" ]]; then | ||
_partition="${_pname%%\:*}" | ||
printf "%s" "${_partition##*/}" | ||
break | ||
fi | ||
done | ||
} | ||
|
||
_get_part_prefix_for_device() { | ||
# some block device type will have "p" or "part" partition number | ||
# prefix associated with it, this function return's the partition prefix | ||
# arguments - name of a partition that can be found in /proc/partitions | ||
# and belongs to the device/disk under analysis | ||
local _partition | ||
_partition="$1" | ||
if [[ "${_partition}" =~ nvme|loop|mmcblk ]]; then | ||
if [[ "${_partition}" =~ .*part.* ]]; then | ||
printf "part" | ||
else | ||
printf "p" | ||
fi | ||
fi | ||
} | ||
|
||
_count_parts_for_disk() { | ||
# Match how many partitions belong to a given disk based on | ||
# data available in /proc/partitions | ||
# arguments - name of the disk under /dev (not path just name) | ||
local _count _disk | ||
_disk="$1" | ||
# both the disk and the partitions are listed in the /proc/partitions so | ||
# there will be an extra record that has to be accounted for | ||
_count=-1 | ||
# read command will also cut up the columns | ||
while read -r _ _ _ _part; do | ||
# if the major device number matches then we have match | ||
if [[ "${_part}" =~ ${_disk} ]]; then | ||
_count=$((_count + 1)) | ||
fi | ||
done < "/proc/partitions" | ||
printf "%s" "${_count}" | ||
} | ||
|
||
_dry_run() { | ||
# if dry_run mode is enabled commands are just printed not executed | ||
# arguments - anything | ||
if [[ "${dry_run}" == "false" ]]; then | ||
"$@" | ||
else | ||
printf "DRY_RUN: " | ||
printf "%s " "${@}" | ||
printf "\n" | ||
fi | ||
} | ||
|
||
# Start of the execution | ||
|
||
root_device_path="${1:?}" | ||
key_script="${2:-/etc/tpm2-unseal-key.sh}" | ||
config_drive_part_num="${3:-}" | ||
dry_run="${4:-false}" | ||
key="${key_script}" | ||
|
||
printf "INFO: unlock script has been started with the following arguments:\n" | ||
printf "Root device:%s, key script:%s, config-drive partition number:%s, " \ | ||
"${root_device_path}" "${key_script}" "${config_drive_part_num}" | ||
printf "dry run:%s\n" "${dry_run}" | ||
|
||
# create the mount point for the root file system and evaluate the | ||
# key script | ||
if [[ "${dry_run}" == "false" ]]; then | ||
key="<(${key_script})" | ||
mkdir "/realroot" | ||
fi | ||
|
||
# different workflows depending on the presence of encryption | ||
# -------------------------------------------------------------------- | ||
# nvme, mmc/sd card and loop devices have a partition number prefix | ||
# e.g./dev/nvme0n1 <--> /dev/nvme0n1p1 | ||
# -------------------------------------------------------------------- | ||
# It is expected that the last partition on the root device is the | ||
# config drive partition thus that is the default logic. In case the user | ||
# specified a value for `config_drive_part_num` the partition count will | ||
# be discarded and the value of `config_drive_part_num` will be used instead. | ||
if _is_luks "${root_device_path}"; then | ||
printf "Mounting encrypted %s\n" "${root_device_path}" | ||
_dry_run "/usr/lib/systemd/systemd-cryptsetup" "attach" "realroot" \ | ||
"${root_device_path}" "${key}" "luks" | ||
_dry_run "mount" "/dev/mapper/realroot" "/realroot" | ||
root_partition="$(_get_partition_from_blkid "${root_device_path}")" | ||
part_prefix="$(_get_part_prefix_for_device "${root_partition}")" | ||
root_device="${root_partition%"${part_prefix}"*}" | ||
part_count="$(_count_parts_for_disk "${root_device}")" | ||
config_drive_common="/dev/${root_device}${part_prefix}" | ||
if [[ -z "${config_drive_part_num}" ]]; then | ||
config_drive_path="${config_drive_common}${part_count}" | ||
else | ||
config_drive_path="${config_drive_common}${config_drive_part_num}" | ||
fi | ||
if _is_luks "${config_drive_path}"; then | ||
printf "Unlocking config drive %s\n" "${config_drive_path}" | ||
printf "INFO: config-drive:%s disk:%s part_count:%s " \ | ||
"${config_drive_path}" "${root_device}" "${part_count}" | ||
printf "root_partition:%s prefix:%s\n" "${root_partition}" \ | ||
"${part_prefix}" | ||
_dry_run "/usr/lib/systemd/systemd-cryptsetup" "attach" "config-2" \ | ||
"${config_drive_path}" "${key}" "luks" | ||
# At this stage the config drive does not need to be mounted, after | ||
# it is unlocked cloud-init can identify and mount the config drive | ||
# on its own | ||
fi | ||
|
||
else | ||
printf "Mounting NON encrypted volume!!!\n" | ||
_dry_run "mount" "${root_device_path}" "/realroot" | ||
fi |