Files
platform-core/scripts/validate/k3s-local-recovery-feasibility.sh

432 lines
17 KiB
Bash

#!/usr/bin/env bash
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
LC_ALL=C
export LC_ALL
umask 077
set -Eeuo pipefail
_k3slrf_initial_guard() {
local candidate_euid="${1-}" shell_flags="${2-}"
[[ "$candidate_euid" =~ ^[0-9]+$ ]] || return 1
(( candidate_euid != 0 )) || return 1
[[ "$shell_flags" != *x* ]]
}
if ! _k3slrf_initial_guard "$EUID" "$-"; then
printf 'Local recovery feasibility refused\n' >&2
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then return 1; else exit 1; fi
fi
readonly K3SLRF_KEEPASSXC='/usr/bin/keepassxc-cli'
readonly K3SLRF_CRYPTSETUP='/usr/sbin/cryptsetup'
readonly K3SLRF_KEEPASS_PACKAGE='keepassxc'
readonly K3SLRF_KEEPASS_VERSION='2.7.6+dfsg.1-1build3'
readonly K3SLRF_CRYPTSETUP_PACKAGE='cryptsetup-bin'
readonly K3SLRF_CRYPTSETUP_VERSION='2:2.7.0-1ubuntu4.2'
readonly K3SLRF_SCRIPT_DIR="$(cd -P -- "${BASH_SOURCE[0]%/*}" && pwd -P)"
readonly K3SLRF_REPOSITORY_ROOT="$(cd -P -- "${K3SLRF_SCRIPT_DIR}/../.." && pwd -P)"
readonly K3SLRF_LIBRARY_PATH="${K3SLRF_REPOSITORY_ROOT}/scripts/lib/k3s-local-recovery.sh"
# shellcheck source=../lib/k3s-local-recovery.sh
builtin source -- "$K3SLRF_LIBRARY_PATH" || {
printf 'Local recovery feasibility refused\n' >&2
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then return 1; else exit 1; fi
}
_k3slrf_usage() {
printf 'Usage: bash scripts/validate/k3s-local-recovery-feasibility.sh [--execute]\n'
}
_k3slrf_fail() {
printf 'Local recovery feasibility failed\n' >&2
return 1
}
_k3slrf_require_interactive_stdin() {
[[ -t 0 ]]
}
_k3slrf_require_cached_sudo() {
_k3slr_cached_sudo_is_available
}
_k3slrf_package_version_is_exact() {
local package="$1" expected="$2" output
output="$(
_k3slr_command /usr/bin/dpkg-query --show --showformat='${db:Status-Abbrev}|${Version}\n' -- "$package" 2>/dev/null
printf '\034%s' "$?"
)" || return 1
[[ "$output" == "ii |${expected}"$'\n'$'\0340' ]]
}
_k3slrf_verify_prerequisites() {
_k3slr_trusted_root_executable /usr/bin/dpkg-query || return 1
_k3slr_trusted_root_executable /usr/bin/mktemp || return 1
_k3slr_trusted_root_executable /usr/bin/rm || return 1
_k3slr_trusted_root_executable /usr/bin/cmp || return 1
_k3slr_trusted_root_executable /usr/bin/mawk || return 1
_k3slr_trusted_root_executable /usr/bin/od || return 1
_k3slr_trusted_root_executable /usr/bin/ps || return 1
_k3slr_trusted_root_executable /usr/bin/sleep || return 1
_k3slr_trusted_root_executable /usr/bin/sha256sum || return 1
_k3slr_trusted_root_executable "$K3SLRF_KEEPASSXC" || return 1
_k3slr_trusted_root_executable "$K3SLRF_CRYPTSETUP" || return 1
_k3slrf_package_version_is_exact "$K3SLRF_KEEPASS_PACKAGE" "$K3SLRF_KEEPASS_VERSION" || return 1
_k3slrf_package_version_is_exact "$K3SLRF_CRYPTSETUP_PACKAGE" "$K3SLRF_CRYPTSETUP_VERSION"
}
_k3slrf_create_fixture_dir() {
local destination_name="$1" identity_name="$2" owned_name="$3" created physical metadata uid
created="$(_k3slr_command /usr/bin/mktemp -d /tmp/k3slr-feasibility.XXXXXX)" || return 1
printf -v "$destination_name" '%s' "$created"
printf -v "$owned_name" '%s' true
[[ "$created" =~ ^/tmp/k3slr-feasibility\.[A-Za-z0-9]+$ ]] || return 1
_k3slr_command /usr/bin/test -d "$created" || return 1
_k3slr_command /usr/bin/test ! -L "$created" || return 1
physical="$(cd -P -- "$created" && pwd -P)" || return 1
[[ "$physical" == "$created" ]] || return 1
_k3slr_capture_one_line uid /usr/bin/id -u || return 1
_k3slr_capture_one_line metadata /usr/bin/stat --format='%u|%a|%F|%d:%i' -- "$created" || return 1
[[ "$metadata" == "${uid}|700|directory|"* ]] || return 1
printf -v "$identity_name" '%s' "${metadata##*|}"
}
_k3slrf_cleanup_fixture_dir() {
local fixture_dir="$1" expected_identity="$2" metadata uid
[[ "$fixture_dir" =~ ^/tmp/k3slr-feasibility\.[A-Za-z0-9]+$ ]] || return 1
_k3slr_command /usr/bin/test -d "$fixture_dir" || return 1
_k3slr_command /usr/bin/test ! -L "$fixture_dir" || return 1
_k3slr_capture_one_line uid /usr/bin/id -u || return 1
_k3slr_capture_one_line metadata /usr/bin/stat --format='%u|%a|%F|%d:%i' -- "$fixture_dir" 2>/dev/null || return 1
[[ "$metadata" == "${uid}|700|directory|"* ]] || return 1
[[ -z "$expected_identity" || "${metadata##*|}" == "$expected_identity" ]] || return 1
_k3slr_command /usr/bin/rm --recursive --one-file-system -- "$fixture_dir"
}
_k3slrf_synthetic_master_once_stdout() {
printf '%s' 'k3slr-package-feasibility-master-v1' |
_k3slr_command /usr/bin/sha256sum |
_k3slr_command /usr/bin/mawk '{ print $1 }'
}
_k3slrf_synthetic_master_twice_stdout() {
_k3slrf_synthetic_master_once_stdout
_k3slrf_synthetic_master_once_stdout
}
_k3slrf_validate_generated_password_stdin() {
_k3slr_command /usr/bin/od -An -v -tx1 |
_k3slr_command /usr/bin/mawk '
{
for (i=1; i<=NF; i++) {
n++; byte=$i
if (n == 41) { if (byte != "0a") bad=1 }
else if (byte ~ /^3[0-9]$/) digit=1
else if (byte ~ /^(4[1-9a-f]|5[0-9a])$/) upper=1
else if (byte ~ /^(6[1-9a-f]|7[0-9a])$/) lower=1
else bad=1
}
}
END { exit !(n == 41 && !bad && lower && upper && digit) }
'
}
_k3slrf_file_identity() {
local destination_name="$1" path="$2" metadata uid
_k3slr_command /usr/bin/test -f "$path" || return 1
_k3slr_command /usr/bin/test ! -L "$path" || return 1
_k3slr_capture_one_line uid /usr/bin/id -u || return 1
_k3slr_capture_one_line metadata /usr/bin/stat --format='%u|%a|%F|%d:%i' -- "$path" || return 1
[[ "$metadata" == "${uid}|600|regular file|"* ||
"$metadata" == "${uid}|600|regular empty file|"* ]] || return 1
printf -v "$destination_name" '%s' "${metadata##*|}"
}
_k3slrf_file_identity_matches() {
local current=''
_k3slrf_file_identity current "$1" || return 1
[[ "$current" == "$2" ]]
}
_k3slrf_directory_identity() {
local destination_name="$1" path="$2" physical metadata uid
[[ "$path" =~ ^/tmp/k3slr-feasibility\.[A-Za-z0-9]+$ ]] || return 1
_k3slr_command /usr/bin/test -d "$path" || return 1
_k3slr_command /usr/bin/test ! -L "$path" || return 1
physical="$(cd -P -- "$path" && pwd -P)" || return 1
[[ "$physical" == "$path" ]] || return 1
_k3slr_capture_one_line uid /usr/bin/id -u || return 1
_k3slr_capture_one_line metadata /usr/bin/stat --format='%u|%a|%F|%d:%i' -- "$path" || return 1
[[ "$metadata" == "${uid}|700|directory|"* ]] || return 1
printf -v "$destination_name" '%s' "${metadata##*|}"
}
_k3slrf_directory_identity_matches() {
local current=''
_k3slrf_directory_identity current "$1" || return 1
[[ "$current" == "$2" ]]
}
_k3slrf_run_tracked() {
local rc caller_group='' current_identity='' stopped_identity=''
_k3slr_process_group_for_pid caller_group "$BASHPID" || return 1
K3SLRF_ACTIVE_CHILD_STATE='launching'
K3SLRF_LAST_CLEANUP_OK=false
_k3slr_lifecycle_boundary feasibility-launch-before
_k3slrf_pending_checkpoint || return $?
set -m
(
tracked_cancel=''
trap '[[ "$tracked_cancel" == TERM ]] || tracked_cancel=INT' INT
trap 'tracked_cancel=TERM' TERM
kill -STOP "$BASHPID"
tracked_worker_rc=143
if [[ -z "$tracked_cancel" ]]; then
if "$@"; then tracked_worker_rc=0; else tracked_worker_rc=$?; fi
fi
while [[ -n "$tracked_cancel" ]]; do
_k3slr_command /usr/bin/sleep 0.01 || :
done
exit "$tracked_worker_rc"
) &
K3SLRF_ACTIVE_CHILD_PID=$!
K3SLRF_ACTIVE_CHILD_STATE='published'
_k3slr_lifecycle_boundary feasibility-pid-published "$K3SLRF_ACTIVE_CHILD_PID"
if ! _k3slr_process_identity K3SLRF_ACTIVE_CHILD_IDENTITY "$K3SLRF_ACTIVE_CHILD_PID"; then
set +m
_k3slrf_cleanup_active_child || return 1
if [[ -n "$K3SLRF_PENDING_SIGNAL" ]]; then return "$K3SLRF_PENDING_STATUS"; fi
return 1
fi
if ! _k3slr_wait_for_stopped_group K3SLRF_ACTIVE_CHILD_PGID stopped_identity "$K3SLRF_ACTIVE_CHILD_PID" ||
[[ "$stopped_identity" != "$K3SLRF_ACTIVE_CHILD_IDENTITY" ]]; then
set +m
_k3slrf_cleanup_active_child || return 1
if [[ -n "$K3SLRF_PENDING_SIGNAL" ]]; then return "$K3SLRF_PENDING_STATUS"; fi
return 1
fi
K3SLRF_ACTIVE_CHILD_STATE='stopped_pinned'
_k3slr_lifecycle_boundary feasibility-stop-query-complete
_k3slrf_pending_checkpoint || { rc=$?; set +m; return "$rc"; }
if [[ "$K3SLRF_ACTIVE_CHILD_PGID" == "$caller_group" ]]; then
set +m
_k3slrf_cleanup_active_child || return 1
return 1
fi
set +m
_k3slr_lifecycle_boundary feasibility-cont-before
_k3slrf_pending_checkpoint || return $?
_k3slr_continue_owned_child "$K3SLRF_ACTIVE_CHILD_PID" "$K3SLRF_ACTIVE_CHILD_PGID" \
"$K3SLRF_ACTIVE_CHILD_IDENTITY" || return 1
K3SLRF_ACTIVE_CHILD_STATE='continued'
_k3slr_lifecycle_boundary feasibility-cont-after
_k3slrf_pending_checkpoint || return $?
K3SLRF_ACTIVE_CHILD_STATE='waiting'
if _k3slr_wait_child "$K3SLRF_ACTIVE_CHILD_PID"; then rc=0; else rc=$?; fi
_k3slr_lifecycle_boundary feasibility-wait-reaped-before-clear
if _k3slr_process_identity current_identity "$K3SLRF_ACTIVE_CHILD_PID" 2>/dev/null &&
[[ "$current_identity" == "$K3SLRF_ACTIVE_CHILD_IDENTITY" ]]; then
_k3slrf_cleanup_active_child || return 1
fi
K3SLRF_ACTIVE_CHILD_STATE='reaped' K3SLRF_ACTIVE_CHILD_PID='' \
K3SLRF_ACTIVE_CHILD_PGID='' K3SLRF_ACTIVE_CHILD_IDENTITY=''
K3SLRF_LAST_CLEANUP_OK=true
_k3slr_lifecycle_boundary feasibility-state-cleared
if [[ -n "$K3SLRF_PENDING_SIGNAL" ]]; then
rc="$K3SLRF_PENDING_STATUS"
fi
return "$rc"
}
_k3slrf_db_create() {
_k3slrf_synthetic_master_twice_stdout |
_k3slr_command "$K3SLRF_KEEPASSXC" db-create --quiet --set-password "$1" >/dev/null 2>&1
}
_k3slrf_entry_add() {
_k3slrf_synthetic_master_once_stdout |
_k3slr_command "$K3SLRF_KEEPASSXC" add --quiet --generate --length 40 \
--lower --upper --numeric --every-group "$1" "$K3SLR_KEEPASS_ENTRY" >/dev/null 2>&1
}
_k3slrf_password_check() {
_k3slrf_synthetic_master_once_stdout |
_k3slr_command "$K3SLRF_KEEPASSXC" show --show-protected \
--attributes Password "$1" "$K3SLR_KEEPASS_ENTRY" 2>/dev/null |
_k3slrf_validate_generated_password_stdin
}
_k3slrf_attachment_listing_stdout() {
(( $# == 1 )) || return 1
_k3slrf_synthetic_master_once_stdout |
_k3slr_command "$K3SLRF_KEEPASSXC" show --quiet --attributes Title \
--show-attachments "$1" "$K3SLR_KEEPASS_ENTRY"
}
_k3slrf_attachment_state_is() {
local database="${1-}" fixture_dir="${2-}" expected_state="${3-}"
local capture_base='' record='' state=''
(( $# == 3 )) || return 1
[[ "$expected_state" == absent || "$expected_state" == present ]] || return 1
capture_base="${fixture_dir}/keepass-listing.${BASHPID}"
_k3slr_capture_keepass_attachment_listing record \
"${capture_base}.stdout" "${capture_base}.stderr" \
_k3slrf_attachment_listing_stdout "$database" || return 1
_k3slr_parse_keepass_attachment_listing "$record" fixture.bin state || return 1
[[ "$state" == "$expected_state" ]]
}
_k3slrf_attachment_import() {
_k3slrf_synthetic_master_once_stdout |
_k3slr_command "$K3SLRF_KEEPASSXC" attachment-import --quiet "$1" "$K3SLR_KEEPASS_ENTRY" \
fixture.bin "$2" >/dev/null 2>&1
}
_k3slrf_attachment_export() {
_k3slrf_synthetic_master_once_stdout |
_k3slr_command "$K3SLRF_KEEPASSXC" attachment-export --quiet "$1" "$K3SLR_KEEPASS_ENTRY" \
fixture.bin "$2" >/dev/null 2>&1
}
_k3slrf_run_synthetic() {
local fixture_dir="$1" database input_file output_file database_identity input_identity output_identity fixture_identity export_fd
database="${fixture_dir}/synthetic.kdbx"
input_file="${fixture_dir}/input.bin"
output_file="${fixture_dir}/output.bin"
[[ ! -e "$database" && ! -L "$database" && ! -e "$input_file" && ! -L "$input_file" &&
! -e "$output_file" && ! -L "$output_file" ]] || return 1
_k3slrf_directory_identity fixture_identity "$fixture_dir" || return 1
( set -o noclobber; printf '%s\n' 'k3slr-attachment-roundtrip-v1' >"$input_file" ) || return 1
_k3slrf_file_identity input_identity "$input_file" || return 1
_k3slrf_run_tracked _k3slrf_db_create "$database" || return 1
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity database_identity "$database" || return 1
_k3slrf_run_tracked _k3slrf_entry_add "$database" || return 1
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity database_identity "$database" || return 1
_k3slrf_run_tracked _k3slrf_password_check "$database" || return 1
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity_matches "$database" "$database_identity" || return 1
_k3slrf_file_identity_matches "$input_file" "$input_identity" || return 1
_k3slrf_run_tracked _k3slrf_attachment_state_is "$database" "$fixture_dir" absent || return 1
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity_matches "$database" "$database_identity" || return 1
_k3slrf_file_identity_matches "$input_file" "$input_identity" || return 1
_k3slrf_run_tracked _k3slrf_attachment_import "$database" "$input_file" || return 1
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity database_identity "$database" || return 1
_k3slrf_run_tracked _k3slrf_attachment_state_is "$database" "$fixture_dir" present || return 1
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity_matches "$database" "$database_identity" || return 1
set -o noclobber
if ! exec {export_fd}>"$output_file"; then
set +o noclobber
return 1
fi
set +o noclobber
_k3slrf_file_identity output_identity "$output_file" || return 1
_k3slrf_run_tracked _k3slrf_attachment_export "$database" "/proc/${BASHPID}/fd/${export_fd}" || return 1
exec {export_fd}>&-
_k3slrf_directory_identity_matches "$fixture_dir" "$fixture_identity" || return 1
_k3slrf_file_identity_matches "$database" "$database_identity" || return 1
_k3slrf_file_identity_matches "$output_file" "$output_identity" || return 1
_k3slr_command /usr/bin/cmp --silent -- "$input_file" "$output_file"
}
k3slr_local_recovery_feasibility_main() (
local execute=false fixture_dir='' fixture_identity='' fixture_owned=false
local K3SLRF_ACTIVE_CHILD_PID='' K3SLRF_ACTIVE_CHILD_PGID='' K3SLRF_ACTIVE_CHILD_IDENTITY=''
local K3SLRF_ACTIVE_CHILD_STATE='empty' K3SLRF_PENDING_SIGNAL='' K3SLRF_PENDING_STATUS=''
local K3SLRF_LAST_CLEANUP_OK=false
case "$#" in
0) ;;
1) [[ "$1" == --execute ]] || { _k3slrf_usage >&2; return 2; }; execute=true ;;
*) _k3slrf_usage >&2; return 2 ;;
esac
if "$execute"; then
_k3slrf_require_interactive_stdin || { _k3slrf_fail; return 1; }
fi
_k3slrf_verify_prerequisites || { _k3slrf_fail; return 1; }
if ! "$execute"; then
printf 'Local recovery feasibility prerequisites: pass\n'
return 0
fi
_k3slrf_require_cached_sudo || { _k3slrf_fail; return 1; }
_k3slrf_cleanup_active_child() {
local cleanup_rc=0
if [[ -n "$K3SLRF_ACTIVE_CHILD_PID" ]]; then
if [[ "$K3SLRF_ACTIVE_CHILD_STATE" == published ]]; then
_k3slr_terminate_published_child "$K3SLRF_ACTIVE_CHILD_PID" || cleanup_rc=$?
elif [[ -n "$K3SLRF_ACTIVE_CHILD_IDENTITY" ]]; then
_k3slr_terminate_and_reap "$K3SLRF_ACTIVE_CHILD_PID" "$K3SLRF_ACTIVE_CHILD_PGID" \
"$K3SLRF_ACTIVE_CHILD_IDENTITY" || cleanup_rc=$?
else
cleanup_rc=1
fi
if (( cleanup_rc == 0 )); then
K3SLRF_ACTIVE_CHILD_PID=''
K3SLRF_ACTIVE_CHILD_PGID=''
K3SLRF_ACTIVE_CHILD_IDENTITY=''
K3SLRF_ACTIVE_CHILD_STATE='reaped'
K3SLRF_LAST_CLEANUP_OK=true
fi
fi
return "$cleanup_rc"
}
_k3slrf_signal() {
if [[ "$1" == TERM || "$K3SLRF_PENDING_SIGNAL" != TERM ]]; then
K3SLRF_PENDING_SIGNAL="$1"
K3SLRF_PENDING_STATUS="$2"
fi
}
_k3slrf_pending_checkpoint() {
local signal_status cleanup_rc=0
[[ -n "$K3SLRF_PENDING_SIGNAL" ]] || return 0
signal_status="$K3SLRF_PENDING_STATUS"
if [[ -n "$K3SLRF_ACTIVE_CHILD_PID" ]]; then
[[ -n "$K3SLRF_ACTIVE_CHILD_IDENTITY" ]] || return 1
_k3slrf_cleanup_active_child || cleanup_rc=$?
else
K3SLRF_LAST_CLEANUP_OK=true
fi
(( cleanup_rc == 0 )) || return 1
return "$signal_status"
}
_k3slrf_exit_cleanup() {
local original_status="$1" cleanup_rc=0
trap - EXIT
_k3slrf_cleanup_active_child || cleanup_rc=$?
if "$fixture_owned"; then
_k3slrf_cleanup_fixture_dir "$fixture_dir" "$fixture_identity" >/dev/null 2>&1 || cleanup_rc=1
fi
(( cleanup_rc == 0 )) || exit 1
exit "$original_status"
}
trap '_k3slrf_exit_cleanup "$?"' EXIT
trap '_k3slrf_signal INT 130' INT
trap '_k3slrf_signal TERM 143' TERM
_k3slrf_create_fixture_dir fixture_dir fixture_identity fixture_owned || { _k3slrf_fail; return 1; }
if ! _k3slrf_run_synthetic "$fixture_dir"; then
if [[ -n "$K3SLRF_PENDING_STATUS" && -z "$K3SLRF_ACTIVE_CHILD_PID" &&
"$K3SLRF_LAST_CLEANUP_OK" == true ]]; then
_k3slrf_cleanup_fixture_dir "$fixture_dir" "$fixture_identity" || return 1
fixture_owned=false
trap - EXIT INT TERM
return "$K3SLRF_PENDING_STATUS"
fi
_k3slrf_fail
return 1
fi
_k3slrf_cleanup_fixture_dir "$fixture_dir" "$fixture_identity" || { _k3slrf_fail; return 1; }
fixture_owned=false
trap - EXIT INT TERM
printf 'Local recovery feasibility: pass\n'
)
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
k3slr_local_recovery_feasibility_main "$@"
fi