Encryption Stability and On-Disk Format Fixes

The on-disk format for encrypted datasets protects not only
the encrypted and authenticated blocks themselves, but also
the order and interpretation of these blocks. In order to
make this work while maintaining the ability to do raw
sends, the indirect bps maintain a secure checksum of all
the MACs in the block below it along with a few other
fields that determine how the data is interpreted.

Unfortunately, the current on-disk format erroneously
includes some fields which are not portable and thus cannot
support raw sends. It is not possible to easily work around
this issue due to a separate and much smaller bug which
causes indirect blocks for encrypted dnodes to not be
compressed, which conflicts with the previous bug. In
addition, the current code generates incompatible on-disk
formats on big endian and little endian systems due to an
issue with how block pointers are authenticated. Finally,
raw send streams do not currently include dn_maxblkid when
sending both the metadnode and normal dnodes which are
needed in order to ensure that we are correctly maintaining
the portable objset MAC.

This patch zero's out the offending fields when computing
the bp MAC and ensures that these MACs are always
calculated in little endian order (regardless of the host
system's byte order). This patch also registers an errata
for the old on-disk format, which we detect by adding a
"version" field to newly created DSL Crypto Keys. We allow
datasets without a version (version 0) to only be mounted
for read so that they can easily be migrated. We also now
include dn_maxblkid in raw send streams to ensure the MAC
can be maintained correctly.

This patch also contains minor bug fixes and cleanups.

Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Signed-off-by: Tom Caputi <tcaputi@datto.com>
Closes #6845
Closes #6864
Closes #7052
This commit is contained in:
Tom Caputi
2017-11-08 14:12:59 -05:00
committed by Brian Behlendorf
parent 4c46b99d24
commit ae76f45cda
31 changed files with 787 additions and 187 deletions
+4 -2
View File
@@ -337,7 +337,8 @@ tests = ['zpool_import_001_pos', 'zpool_import_002_pos',
'zpool_import_features_003_pos','zpool_import_missing_001_pos',
'zpool_import_missing_002_pos',
'zpool_import_rename_001_pos', 'zpool_import_all_001_pos',
'zpool_import_encrypted', 'zpool_import_encrypted_load']
'zpool_import_encrypted', 'zpool_import_encrypted_load',
'zpool_import_errata3']
tags = ['functional', 'cli_root', 'zpool_import']
[tests/functional/cli_root/zpool_labelclear]
@@ -655,7 +656,8 @@ tests = ['rsend_001_pos', 'rsend_002_pos', 'rsend_003_pos', 'rsend_004_pos',
'send-c_lz4_disabled', 'send-c_recv_lz4_disabled',
'send-c_mixed_compression', 'send-c_stream_size_estimate', 'send-cD',
'send-c_embedded_blocks', 'send-c_resume', 'send-cpL_varied_recsize',
'send-c_recv_dedup', 'send_encrypted_heirarchy', 'send_freeobjects']
'send-c_recv_dedup', 'send_encrypted_files', 'send_encrypted_heirarchy',
'send_freeobjects']
tags = ['functional', 'rsend']
[tests/functional/scrub_mirror]
@@ -27,10 +27,12 @@ dist_pkgdata_SCRIPTS = \
zpool_import_missing_003_pos.ksh \
zpool_import_rename_001_pos.ksh \
zpool_import_encrypted.ksh \
zpool_import_encrypted_load.ksh
zpool_import_encrypted_load.ksh \
zpool_import_errata3.ksh
BLOCKFILES = \
unclean_export.dat.bz2
unclean_export.dat.bz2 \
cryptv0.dat.bz2
dist_pkgdata_DATA = $(BLOCKFILES)
EXTRA_DIST = $(BLOCKFILES)
@@ -0,0 +1,99 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#
#
# Copyright (c) 2017 Datto, Inc. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# 'zpool import' should import a pool with Errata #3 while preventing
# the user from performing read write operations
#
# STRATEGY:
# 1. Import a pre-packaged pool with Errata #3
# 2. Attempt to write to the effected datasets
# 3. Attempt to read from the effected datasets
# 4. Attempt to perform a raw send of the effected datasets
# 5. Perform a regular send of the datasets under a new encryption root
# 6. Verify the new datasets can be read from and written to
# 7. Destroy the old effected datasets
# 8. Reimport the pool and verify that the errata is no longer present
#
verify_runnable "global"
POOL_NAME=cryptv0
POOL_FILE=cryptv0.dat
function uncompress_pool
{
log_note "Creating pool from $POOL_FILE"
log_must bzcat \
$STF_SUITE/tests/functional/cli_root/zpool_import/$POOL_FILE.bz2 \
> /$TESTPOOL/$POOL_FILE
return 0
}
function cleanup
{
poolexists $POOL_NAME && log_must zpool destroy $POOL_NAME
[[ -e /$TESTPOOL/$POOL_FILE ]] && rm /$TESTPOOL/$POOL_FILE
return 0
}
log_onexit cleanup
log_assert "Verify that Errata 3 is properly handled"
uncompress_pool
log_must zpool import -d /$TESTPOOL/ $POOL_NAME
log_must eval "zpool status | grep -q Errata"
log_must eval "echo 'password' | zfs load-key $POOL_NAME/testfs"
log_must eval "echo 'password' | zfs load-key $POOL_NAME/testvol"
log_mustnot zfs mount $POOL_NAME/testfs
log_must zfs mount -o ro $POOL_NAME/testfs
old_mntpnt=$(get_prop mountpoint $POOL_NAME/testfs)
log_must eval "ls $old_mntpnt | grep -q testfile"
block_device_wait
log_mustnot dd if=/dev/zero of=/dev/zvol/$POOL_NAME/testvol bs=512 count=1
log_must dd if=/dev/zvol/$POOL_NAME/testvol of=/dev/null bs=512 count=1
log_must eval "echo 'password' | zfs create \
-o encryption=on -o keyformat=passphrase -o keylocation=prompt \
cryptv0/encroot"
log_mustnot eval "zfs send -w $POOL_NAME/testfs@snap1 | \
zfs recv $POOL_NAME/encroot/testfs"
log_mustnot eval "zfs send -w $POOL_NAME/testvol@snap1 | \
zfs recv $POOL_NAME/encroot/testvol"
log_must eval "zfs send $POOL_NAME/testfs@snap1 | \
zfs recv $POOL_NAME/encroot/testfs"
log_must eval "zfs send $POOL_NAME/testvol@snap1 | \
zfs recv $POOL_NAME/encroot/testvol"
block_device_wait
log_must dd if=/dev/zero of=/dev/zvol/$POOL_NAME/encroot/testvol bs=512 count=1
new_mntpnt=$(get_prop mountpoint $POOL_NAME/encroot/testfs)
log_must eval "ls $new_mntpnt | grep -q testfile"
log_must zfs destroy -r $POOL_NAME/testfs
log_must zfs destroy -r $POOL_NAME/testvol
log_must zpool export $POOL_NAME
log_must zpool import -d /$TESTPOOL/ $POOL_NAME
log_mustnot eval "zpool status | grep -q Errata"
log_pass "Errata 3 is properly handled"
@@ -23,6 +23,7 @@ dist_pkgdata_SCRIPTS = \
rsend_021_pos.ksh \
rsend_022_pos.ksh \
rsend_024_pos.ksh \
send_encrypted_files.ksh \
send_encrypted_heirarchy.ksh \
send-cD.ksh \
send-c_embedded_blocks.ksh \
@@ -0,0 +1,101 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#
#
# Copyright (c) 2017 by Datto Inc. All rights reserved.
#
. $STF_SUITE/tests/functional/rsend/rsend.kshlib
#
# DESCRIPTION:
#
#
# STRATEGY:
# 1. Create a new encrypted filesystem
# 2. Add an empty file to the filesystem
# 3. Add a small 512 byte file to the filesystem
# 4. Add a larger 32M file to the filesystem
# 5. Add a large sparse file to the filesystem
# 6. Add a file truncated to 4M to the filesystem
# 7. Add a sparse file with metadata compression disabled to the filesystem
# 8. Add and remove 1000 empty files to the filesystem
# 9. Snapshot the filesystem
# 10. Send and receive the filesystem, ensuring that it can be mounted
#
verify_runnable "both"
function set_metadata_compression_disabled # <0|1>
{
echo $1 > /sys/module/zfs/parameters/zfs_mdcomp_disable
}
function cleanup
{
datasetexists $TESTPOOL/$TESTFS2 && \
log_must zfs destroy -r $TESTPOOL/$TESTFS2
datasetexists $TESTPOOL/recv && \
log_must zfs destroy -r $TESTPOOL/recv
[[ -f $keyfile ]] && log_must rm $keyfile
[[ -f $sendfile ]] && log_must rm $sendfile
}
log_onexit cleanup
log_assert "Verify 'zfs send -w' works with many different file layouts"
typeset keyfile=/$TESTPOOL/pkey
typeset sendfile=/$TESTPOOL/sendfile
log_must eval "echo 'password' > $keyfile"
log_must zfs create -o encryption=on -o keyformat=passphrase \
-o keylocation=file://$keyfile $TESTPOOL/$TESTFS2
log_must touch /$TESTPOOL/$TESTFS2/empty
log_must mkfile 512 /$TESTPOOL/$TESTFS2/small
log_must mkfile 32M /$TESTPOOL/$TESTFS2/full
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS2/sparse \
bs=512 count=1 seek=10G >/dev/null 2>&1
log_must mkfile 32M /$TESTPOOL/$TESTFS2/truncated
log_must truncate -s 4M /$TESTPOOL/$TESTFS2/truncated
sync
log_must set_metadata_compression_disabled 1
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS2/no_mdcomp \
count=1 bs=512 seek=10G >/dev/null 2>&1
sync
log_must set_metadata_compression_disabled 0
log_must mkdir -p /$TESTPOOL/$TESTFS2/dir
for i in {1..1000}; do
log_must mkfile 512 /$TESTPOOL/$TESTFS2/dir/file-$i
done
sync
for i in {1..1000}; do
log_must rm /$TESTPOOL/$TESTFS2/dir/file-$i
done
sync
log_must zfs snapshot $TESTPOOL/$TESTFS2@now
log_must eval "zfs send -wR $TESTPOOL/$TESTFS2@now > $sendfile"
log_must eval "zfs recv -F $TESTPOOL/recv < $sendfile"
log_must zfs load-key $TESTPOOL/recv
log_must zfs mount -a
log_pass "Verified 'zfs send -w' works with many different file layouts"