Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ZFS encryption #467

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
add more tests for zfs functions (not complete)
  • Loading branch information
techhazard authored and lowjoel committed Sep 29, 2024
commit 21eab7c92b6bfac836658866b210fc657410a5b0
36 changes: 16 additions & 20 deletions src/zfs/clevis-zfs-common
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ function zfs_get_labels() {
function zfs_set_labels() {
local dataset="${1}";
local new_labels="${2}"
echo >&2 "setting new labels: ${new_labels}"
zfs set "${zfs_labels_prop}=${new_labels}" "${dataset}"
}

Expand Down Expand Up @@ -135,7 +134,7 @@ function zfs_is_bound() {
[[ " ${labels} " == *" ${label} "* ]] && return 0
else
# we don't check for a specific label, just that at least one label is set
[[ "${#current_labels[@]}" -gt 0 ]] && return 0
[[ "${#labels}" -gt 0 ]] && return 0
fi
return 1
}
Expand All @@ -146,13 +145,13 @@ function zfs_is_encryptionroot() {
}

function zfs_test_key() {
local dataset="${1}"
zfs load-key -n -L prompt "${dataset}" &>/dev/null
zfs_load_key "${1}" 'dry_run'
}

function zfs_load_key() {
local dataset="${1}"
zfs load-key -L prompt "${dataset}" &>/dev/null
local dry_run="${2:+-n}"
zfs load-key ${dry_run} -L prompt "${dataset}" >/dev/null
}

function zero_pad() {
Expand Down Expand Up @@ -203,33 +202,30 @@ function zfs_bind_clevis_data() {
zfs_add_label "${dataset}" "${label}"
}

function zfs_get_data_props() {
local dataset="${1}"

#property HAS to be set, otherwise the grep doesn't work
local outputs="${2:-property}"

zfs_get_prop "${dataset}" 'all' -o "${outputs}" | grep -F "${zfs_label_prefix}" | sort
}

function zfs_wipe_clevis_label() {
local dataset="${1}"
local label="${2}"
local label="${2%:*}"
local last_index="${2#*:}"

local zfs_label_prop="${zfs_label_prefix}:${label}"

for prop in $(zfs_get_prop "${dataset}" 'all' -o property | grep -F "${zfs_label_prop}" ); do
zfs inherit "${prop}" "${dataset}"
done
if [[ "${label}" == "${last_index}" ]]; then
zfs inherit "${zfs_label_prop}" "${dataset}"
else
for num in $(seq -w 0 "${last_index}"); do
zfs inherit "${zfs_label_prop}-${num}" "${dataset}"
done
fi
zfs_remove_label "${dataset}" "${label}"
}

function zfs_get_clevis_label() {
local dataset="${1}"
local label="${2%:*}"
local last_index="${2#*:}"

if [[ -z "${last_index}" ]]; then
zfs_label_prop="${zfs_label_prefix}:${label}"
local zfs_label_prop="${zfs_label_prefix}:${label}"
if [[ "${label}" == "${last_index}" ]]; then
zfs_get_prop "${dataset}" "${zfs_label_prop}"
else
for num in $(seq -w 0 "${last_index}"); do
Expand Down
31 changes: 17 additions & 14 deletions src/zfs/clevis-zfs-list
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,34 @@ set -euo pipefail

. clevis-zfs-common

SUMMARY="List zfs datasets that are bound with clevis"


SUMMARY="List zfs datasets that are bound with clevis [in dataset]"

function usage() {
cat >&2 <<-USAGE_END
Usage: clevis zfs list [pool]
Usage: clevis zfs list -d DATASET

$SUMMARY:

-f Do not prompt when overwriting configuration

-d DEV The zfs dataset on which to perform binding

-k KEY Non-interactively read zfs password from KEY file
-k - Non-interactively read zfs password from standard input

USAGE_END
}


main() {
local poolname="${1:-}"
echo "The following ZFS datasets have been bound with clevis:"
zfs list -o "name,${zfs_status_prop}" -r ${poolname} | awk '{ if ($2=="bound") {print $1}}' | sort | sed 's/^/ /'
if [ $# -eq 1 -a "${1:-}" == "--summary" ]; then
echo "$SUMMARY"
exit 0
fi

local dataset
while getopts "d:" o; do
case "$o" in
d) dataset="$OPTARG" ;;
*) error "unrecognized argument: -${OPTARG}" ;;
esac
done

echo >&2 "The following ZFS datasets have been bound with clevis:"
zfs get -r -H -o name,value -slocal "${zfs_labels_prop}" ${dataset}
}

main "${@}"
192 changes: 192 additions & 0 deletions src/zfs/clevis-zfs-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#!/bin/bash
set -euo pipefail

zpool='clevis-zfs-pool'
root_dataset="${zpool}/clevis"
test_dataset="${root_dataset}/test"
tang_host='TANG_HOST'
tang_thp='TANG_THP'

tang_host='192.168.178.26:8565'
tang_thp='NHt3FdBvUX0AiHZycp2emEzfYIc'

function zfs_usage() {
local permissions='create,destroy,mount,load-key,change-key,userprop,encryption,keyformat,keylocation'
cat >&2 <<-EOF

To test the zfs functions you will need to do the following as root:

1) make sure the zfs kernel module is loaded:
modprobe zfs

2) create a new backing file:
dd if=/dev/zero bs=10M count=20 conv=fdatasync of=/tmp/clevis-zfs-pool

3) create an unencrypted zfs pool using the backing file:
zpool create ${zpool} /tmp/clevis-zfs-pool

4) create an unencrypted child dataset so we can grant access to a non-root user:
zpool create ${root_dataset}

4) give the user running the test script permissions to make changes to this pool:
zfs allow ${USER} ${permissions} ${root_dataset}
EOF
}

exit_code=0
testing_password="paaaassssswoooooorrrrddd"

function testing() {
echo >&2 -en "${FUNCNAME[1]}: ${*}... "
}
function success() {
echo >&2 'ok'
}
function failed() {
echo >&2 "failed!"
echo >&2 "${BASH_SOURCE[0]}:${BASH_LINENO[0]} EXPECTED: '${expected}' GOT: '${result}' ${*}"
declare -g exit_code=1
}

function _test() {
_test_is_valid_label
_test_read_key
_test_zero_pad
_test_zfs_functions
[[ "${exit_code}" -gt 0 ]] && echo >&2 "SOME TESTS HAVE FAILED"
exit "${exit_code}"
}



function _test_is_valid_label() {
local expected
local result
valid_labels=(
2 # single digit
0 # single 0
a # single letter
3a # double
a4 # double
1024 # all digits
abcd # all letters
0a0ab # alphanum
a_bc # with underscore
)

invalid_labels=(
a-bc # with dash
a.bc # with dot
a:bc # with colon
a.1_c-2:e # with all
'' # empty string
'.' # just a dot
'-' # just a dash
AteA_ # capitals
aa@a # @-symbol
aa/a # /-symbol
aa/a # /-symbol
aa?a # ?-symbol
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # 101-chars long
)

testing 'testing valid labels'
expected=0
result=1
for l in "${valid_labels[@]}"; do
if ! is_valid_label "${l}" &>/dev/null; then
failed "for is_valid_label '${l}'"
fi
done
success

testing 'testing invalid labels'
expected=1
result=0
for l in "${invalid_labels[@]}"; do
if is_valid_label "${l}" &>/dev/null; then
failed "for \`is_valid_label '${l}'\`"
fi
done
success
}


function _test_read_key() {
# test with reading from stdin
local expected
local result

testing 'key test: no key argument (stdin)'
expected='no-argument-password'
result="$(read_key "mydataset" '' <<<"${expected}" 2>/dev/null)"
[[ "${expected}" == "${result}" ]] && success || failed

testing 'key test: dash argument (stdin)'
expected='dash-argument-password'
result="$(read_key "mydataset" '-' <<<"${expected}" 2>/dev/null)"
[[ "${expected}" == "${result}" ]] && success || failed


testing 'key arg: filename'
expected='filename-argument-password'
result="$(read_key "mydataset" <(echo "${expected}") 2>/dev/null)"
[[ "${expected}" == "${result}" ]] && success || failed
}


function _test_zero_pad() {
testing 'pad with 4 zeroes'
local expected='000051'
local result="$(zero_pad 51 6)"
[[ "${expected}" == "${result}" ]] && success || failed
}


function _zfs_test_teardown() {
testing "removing zfs testing dataset: ${test_dataset}"
! zfs list "${test_dataset}" &>/dev/null && success && return 0
zfs destroy -f -r "${test_dataset}"
success
}

function _zfs_create_encrypted_dataset() {
local dataset="${1}"
testing "creating encrypted test dataset: ${1}"
# zfs create will work with the permissions as described, but will exit
# with an error code because mounting failed
zfs create "${1}" -o encryption=on -o keyformat=passphrase <<<"${testing_password}" &>/dev/null || true
# we need to make sure the dataset is created because of the error-code shenanigans
zfs list "${1}" &>/dev/null && success || return 1
}

function _test_zfs_functions() {
testing "checking if zfs testing dataset exists: ${root_dataset}"
zfs list "${root_dataset}" &>/dev/null && success || (zfs_usage; return 1)
trap _zfs_test_teardown EXIT
_zfs_create_encrypted_dataset "${test_dataset}"

testing 'binding zfs dataset twice'
echo "${testing_password}" | ./clevis-zfs-bind -d "${test_dataset}" -k - -l 'testlabel' tang '{"url": "http://'${tang_host}'", "thp": "'${tang_thp}'"}' &>/dev/null
echo "${testing_password}" | ./clevis-zfs-bind -d "${test_dataset}" -k - -l 'another_testlabel' tang '{"url": "http://'${tang_host}'", "thp": "'${tang_thp}'"}' &>/dev/null
success

testing 'listing labels'
expected="${test_dataset}"$'\ttestlabel:1 another_testlabel:1'
result="$(./clevis-zfs-list -d "${test_dataset}" 2>/dev/null)"
[[ "${expected}" == "${result}" ]] && success || failed

testing 'testing unlocking with bindings'
./clevis-zfs-unlock -t -d "${test_dataset}" && success

testing 'unlocking with binding'
zfs unload-key "${test_dataset}"
[[ "$(zfs_get_prop "${test_dataset}" 'keystatus' -snone)" == 'unavailable' ]] || return 1
./clevis-zfs-unlock -d "${test_dataset}"
success

testing 'unbinding dataset twice'
echo "${testing_password}" | ./clevis-zfs-unbind -d "${test_dataset}" -l 'another_testlabel' -k -
echo "${testing_password}" | ./clevis-zfs-unbind -d "${test_dataset}" -l 'testlabel' -k -
success
}
6 changes: 3 additions & 3 deletions src/zfs/clevis-zfs-unbind
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#!/bin/bash
set -eu
set -euo pipefail

. clevis-zfs-common

SUMMARY="Unbinds a label from a ZFS dataset"



function usage() {
cat >&2 <<-USAGE_END
Usage: clevis zfs unbind [-k KEY] -d DATASET -l LABEL
Expand Down Expand Up @@ -58,7 +57,8 @@ function main() {
fi

echo >&2 -n 'wiping clevis data... '
zfs_wipe_clevis_data "${dataset}" "${label}"
zfs_wipe_clevis_label "${dataset}" "${label}"
echo >&2 'ok'
}

main "${@}"
Loading