Add kdumpctl setup-crypttab subcommand

Resolves: https://issues.redhat.com/browse/RHEL-104940
Conflict: None

commit ddd33c8d0f552cb46097aeade86178266637aa05
Author: Coiby Xu <coxu@redhat.com>
Date:   Tue Sep 16 16:22:18 2025 +0800

    Add kdumpctl setup-crypttab subcommand

    Resolves: https://issues.redhat.com/browse/RHEL-29037
    Relates: https://issues.redhat.com/browse/RHEL-29039

    This subcommand is to add the 'link-volume-key' option to /etc/crypttab
    so the volume keys can be passed to the crash kernel to unlock
    LUKS-encrypted device automatically.

    This API will be also be called by kdump-anaconda-addon to set up
    /etc/crypttab on installation.

    Signed-off-by: Coiby Xu <coxu@redhat.com>
    Assisted-by: Google Gemini

Signed-off-by: Coiby Xu <coxu@redhat.com>
This commit is contained in:
Coiby Xu 2025-10-09 16:52:06 +08:00
parent 936e845423
commit 10253d9e05
3 changed files with 207 additions and 2 deletions

108
kdumpctl
View File

@ -1080,7 +1080,8 @@ _get_luks_key_by_unlock()
while [ "$_attempt" -le "$_max_retries" ]; do
if cryptsetup open "UUID=$_devuuid" DUMMY "--link-vk-to-keyring=@u::%logon:$_key_des" --test-passphrase; then
ddebug "Success: LUKS device unlocked."
dwarn "To avoid manually running kdumpctl, ensure the link-volume-key=@u::%logon:$_key_des option is correctly set up in /etc/crypttab (see man crypttab)."
# For an interactive terminal, it's possible an user manually entered a passphrase
[ -t 0 ] && dwarn "If you typed passphrase to unlock it manually, please run 'kdumpctl setup-crypttab' (see man kdumpctl)."
return 0
fi
_attempt=$((_attempt + 1))
@ -1175,6 +1176,106 @@ start()
dinfo "Starting kdump: [OK]"
}
# @description Updates a crypttab file to add the 'link-volume-key' option for kdump devices.
#
# It gets the list of kdump-related UUIDs by calling the 'get_all_kdump_crypt_dev' function.
# The function is idempotent and performs pre-flight checks before making any modifications.
#
# @exitcode 0 If the update was successful or no changes were needed.
# @exitcode 1 If there is an error (e.g. UUID mismatch).
#
# @stdout No output on success.
# @stderr Progress and error messages are printed to stderr.
CRYPTTAB_FILE=/etc/crypttab
setup_crypttab()
{
local _devuuid uuids_string temp_file
declare -a _luks_devs
if [[ ! -f $CRYPTTAB_FILE ]]; then
dinfo "$CRYPTTAB_FILE doesn't exist, skip setup"
return 0
fi
mapfile -t _luks_devs < <(get_all_kdump_crypt_dev)
if [[ ${#_luks_devs[@]} -eq 0 ]]; then
dinfo "No LUKS-encrypted device found to process. Exiting."
return 0
fi
for _devuuid in "${_luks_devs[@]}"; do
if ! awk -v devuuid="$_devuuid" '!/^[[:space:]]*#/ && $2 == "UUID="devuuid { found=1; exit } END { exit !found }' "$CRYPTTAB_FILE"; then
derror "Device UUID=${_devuuid} doesn't exist."
return 1
fi
done
uuids_string="${_luks_devs[*]}"
temp_file=$(mktemp)
local status_updates
status_updates=$({
awk -v prefix="$LUKS_KEY_PRFIX" -v targets="$uuids_string" '
BEGIN {
split(targets, temp_arr, " ")
for (i in temp_arr) { target_uuids[temp_arr[i]] = 1 }
}
/^#|^$/ { print; next }
{
split($2, uuid_parts, "=")
current_uuid = uuid_parts[2]
if (current_uuid in target_uuids) {
target_option = "link-volume-key=@u::%logon:" prefix current_uuid
if (!index($0, target_option)) {
# If we need to modify, check if we are replacing or adding
if (index($0, "link-volume-key=")) {
print "STATUS:REPLACED:" current_uuid > "/dev/stderr"
} else {
print "STATUS:ADDED:" current_uuid > "/dev/stderr"
}
# Remove any existing, incorrect link-volume-key option
sub(/,?link-volume-key=[^, ]+/, "", $4)
sub(/^,/, "", $4)
# Add the correct option
if (NF < 3 || $3 == "") { $3 = "none" }
if ($4 == "" || $4 == "none") {
$4 = target_option
} else {
$4 = $4 "," target_option
}
}
}
print
}' "$CRYPTTAB_FILE" > "$temp_file"
} 2>&1)
if cmp -s "$CRYPTTAB_FILE" "$temp_file"; then
dinfo "No changes were needed. $CRYPTTAB_FILE is already up to date."
rm "$temp_file"
return 0
else
mv "$temp_file" "$CRYPTTAB_FILE"
dinfo "Success! $CRYPTTAB_FILE has been updated."
# Parse status updates and report on each changed UUID
while IFS=: read -r _ status uuid; do
if [[ $status == "ADDED" ]]; then
dinfo " - Added link-volume-key for UUID: $uuid"
elif [[ $status == "REPLACED" ]]; then
dinfo " - Replaced link-volume-key for UUID: $uuid"
fi
done < <(echo "$status_updates" | grep "STATUS:")
dinfo "If the dump target is rootfs and you call this command manually, you will need to run 'dracut -f --regenerate-all' to regenerate initramfs."
return 0
fi
}
reload()
{
if ! is_kernel_loaded "$DEFAULT_DUMP_MODE"; then
@ -2127,8 +2228,11 @@ main()
reset_crashkernel_for_installed_kernel "$2"
fi
;;
setup-crypttab)
setup_crypttab
;;
*)
dinfo $"Usage: $0 {estimate|start|stop|status|restart|reload|rebuild|reset-crashkernel|propagate|showmem|test}"
dinfo $"Usage: $0 {estimate|start|stop|status|restart|reload|rebuild|reset-crashkernel|propagate|showmem|test|setup-crypttab}"
exit 1
;;
esac

View File

@ -76,6 +76,17 @@ by "kdumpctl status". Note, fadump is not supported.
If the optional parameter [--force] is provided, there will be no confirmation
before triggering the system crash. Dangerous though, this option is meant
for automation testing.
.TP
.I setup-crypttab
Add the 'link-volume-key' option to /etc/crypttab so vmcore can be saved to
LUKS-encrypted disk volume. For more info on link-volume-key option,
please "man crypttab".
This step is optional. If kdump.service can start successfully, there is no
need for this step. You can confirm if this step is needed by running "kdumpctl
restart". If you need to input any passpharse to unlock the the encrypted
volume, please run "kdumpctl setup-crypttab".
.SH "SEE ALSO"
.BR kdump.conf (5),
.BR mkdumprd (8)

View File

@ -0,0 +1,90 @@
#!/bin/bash
Describe "kdumpctl "
Include ./kdumpctl
# dinfo is a bit complex for unit tets, simply mock it
dinfo() {
echo "$1"
}
Describe "setup_crypttab()"
# Set up global variables and mocks for each test
# shellcheck disable=SC2016 # expand expression later
BeforeEach 'CRYPTTAB_FILE=$(mktemp)'
# shellcheck disable=SC2016 # expand expression later
AfterEach 'rm -f "$CRYPTTAB_FILE"'
Context "when everything is correct"
It "adds link-volume-key to specified UUIDs"
# Arrange
get_all_kdump_crypt_dev() {
echo "uuid-001"
echo "uuid-003"
echo "uuid-005"
echo "uuid-006"
echo "uuid-007"
}
cat >"$CRYPTTAB_FILE" <<EOF
luks-001 UUID=uuid-001 none discard
luks-002 UUID=uuid-002 none discard
# only two mandatory fields
luks-003 UUID=uuid-003
# two mandatory fields + one optional field
luks-005 UUID=uuid-005 -
# tab as delimiter
luks-006 UUID=uuid-006
luks-007 UUID=uuid-007 none discard,link-volume-key=TO_RE_REPLACED
EOF
When call setup_crypttab
The status should be success
The output should include "Success! $CRYPTTAB_FILE has been updated."
The output should include "to run 'dracut -f --regenerate-all'"
The file "$CRYPTTAB_FILE" should be file
The contents of file "$CRYPTTAB_FILE" should eq \
"luks-001 UUID=uuid-001 none discard,link-volume-key=@u::%logon:${LUKS_KEY_PRFIX}uuid-001
luks-002 UUID=uuid-002 none discard
# only two mandatory fields
luks-003 UUID=uuid-003 none link-volume-key=@u::%logon:${LUKS_KEY_PRFIX}uuid-003
# two mandatory fields + one optional field
luks-005 UUID=uuid-005 - link-volume-key=@u::%logon:${LUKS_KEY_PRFIX}uuid-005
# tab as delimiter
luks-006 UUID=uuid-006 none link-volume-key=@u::%logon:${LUKS_KEY_PRFIX}uuid-006
luks-007 UUID=uuid-007 none discard,link-volume-key=@u::%logon:${LUKS_KEY_PRFIX}uuid-007"
End
End
Context "with safety checks and edge cases"
It "succeeds if no LUKS device used for kdump"
get_all_kdump_crypt_dev() { return 0; }
echo "luks-001 UUID=uuid-001" >"$CRYPTTAB_FILE"
When call setup_crypttab
The status should be success
The output should include "No LUKS-encrypted device found to process. Exiting."
End
It "aborts if target UUID is not in crypttab"
get_all_kdump_crypt_dev() { echo "uuid-nonexistent"; }
echo "luks-001 UUID=uuid-001" >"$CRYPTTAB_FILE"
When call setup_crypttab
The status should be failure
The stderr should include "Device UUID=$(get_all_kdump_crypt_dev) doesn't exist"
End
It "aborts if target UUID is only in a commented-out line"
get_all_kdump_crypt_dev() { echo "uuid-001"; }
echo "#luks-001 UUID=uuid-001" >"$CRYPTTAB_FILE"
When call setup_crypttab
The status should be failure
The stderr should include "Device UUID=$(get_all_kdump_crypt_dev) doesn't exist"
End
It "succeeds with no changes if crypttab is already correct"
get_all_kdump_crypt_dev() { echo "uuid-001"; }
echo "luks-001 UUID=uuid-001 none link-volume-key=@u::%logon:${LUKS_KEY_PRFIX}uuid-001" >"$CRYPTTAB_FILE"
When call setup_crypttab
The status should be success
The output should include "No changes were needed."
End
End
End
End