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

Allow block cloning across encrypted datasets. Rebase #14705 to master, added tests. #15544

Merged
merged 3 commits into from
Dec 5, 2023

Conversation

oromenahar
Copy link
Contributor

@oromenahar oromenahar commented Nov 19, 2023

Block cloning is now possible between two encrypted datasets that share the same encryption root.
This is a rebase of #14705 from @pjd and new tests. Sorry don't know how to add new commits to the old PR.

Motivation and Context

Tests are important for this feature. And we can fix a lot with good tests, before handling it to any user.

Description

The tests are described in the commit message.
There is a problem, with childs and siblings. They don't share the same encryption root. Every master key contains a key, an init Vector and a hmac. I guess that the init Vector and/or hmac changes when creating a child. But wanna throw this against the full pipeline. I'm working currently on encryption and key handling, but there is not that much time for me. Until I figure this out, it's just a quick guess without any deep dive and I wanna see the pipeline results.

Missing tests

If I forgot to test something just describe the test and I'm going to add it later.

  • cloning from non-encrypted to encrypted datasets
  • cloning from encrypted to non-encrypted datasets
  • cloning from a snapshot dir to a filesystem that is not the source of that snapshot
  • cloning from a filesystem created from a snapshot clone to another unrelated dataset
  • moving dataset to a new encryption root
  • And so on. Probably some positive versions of these too.
  • send | recv (change key) -> clone

How Has This Been Tested?

By running the new test for this.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Library ABI change (libzfs, libzfs_core, libnvpair, libuutil and libzfsbootenv)
  • Documentation (a change to man pages or other documentation)

Checklist:

@oromenahar
Copy link
Contributor Author

The important case to recover a file from a snapshot is working. So happy recovering files from snapshots.

@robn
Copy link
Member

robn commented Nov 19, 2023

No. See analysis in #14705 (comment) . Cloning encrypted blocks is only possible if they share the same master key, which it can only be between a dataset, its snapshots, and cloned datasets created from those snapshots. The encryption root is irrelevant, as that's only the holder of the wrapping key used to unlock the master key (datasets can be moved to another encryption root).

@mmatuska
Copy link
Contributor

mmatuska commented Nov 19, 2023

We cannot check against the common encryption root.

Here is a test that makes this not work:

zfs create -o mountpoint=none -o encryption=on -o keyformat=passphrase rpool/ENC1
zfs create rpool/ENC1/ds1
zfs create -o mountpoint=none -o encryption=on -o keyformat=passphrase rpool/ENC2
zfs create rpool/ENC2/ds2
zfs change-key -o keyformat=passphrase rpool/ENC2/ds2
zfs rename rpool/ENC2/ds2 rpool/ENC1/ds2
zfs change-key -i rpool/ENC1/ds2

Now the datasets rpool/ENC1/ds1 and rpool/ENC1/ds2 are under the same encryption root but have different master keys.
Now just copy_file_range(2) a file from ds1 to ds2 and you get a read error when reading the file from ds2.

The safest way would be to compare master keys (or check if the same master key is used), the second safest to allow only snapshots and clones of the same dataset because they have to use the same key.

@scineram
Copy link

Encrypted dedup already works only within clone family (same master key). How does the check for that work?

@mmatuska
Copy link
Contributor

@oromenahar @robn I have created #15545 that does the right job IMO

@oromenahar
Copy link
Contributor Author

oromenahar commented Nov 19, 2023

I have updated my PR with @mmatuska code. The tests are sucessfull.
There is a function called spa_crypto_key_compare I think we should use this to compare the crypto_keys.

@mmatuska
Copy link
Contributor

I have updated my PR with @mmatuska code. The tests are sucessfull. There is a function called spa_crypto_key_compare I think we should use this to compare the crypto_keys.

I agree we can use spa_crypto_key_compare().
But this functions returns -1, 0 or 1, so we should probably change:

if (spa != dmu_objset_spa(osb))
	return (1);

to this:

if (spa != dmu_objset_spa(osb))
	return (SET_ERROR(EINVAL));

For debugging purposes.

@oromenahar
Copy link
Contributor Author

oromenahar commented Nov 19, 2023

if (spa != dmu_objset_spa(osb))
	return (1);

do we even need to check if the spa is the same? And I would say even if they are not the same, the crypto key can't be the same, so the comparing is doing what I expect. Telling me, thats different doesn't matter on what condition. It don't tells you exactly why it's not the same. But this can have several reasons.

If I would change something, it would be

	ret = spa_keystore_lookup_key(spa, obja, FTAG, &dcka);
	if (ret != 0)
		return (ret);

to

	ret = spa_keystore_lookup_key(spa, obja, FTAG, &dcka);
	if (ret != 0)
		return (1);

Or even change it to boolean_t as a return value. Which is just true or false without any error.

All of this is not how the return values of for example memcmp function works but the closest possible solution.

I'm curious to receive your feedback @mmatuska .

@robn
Copy link
Member

robn commented Nov 19, 2023

I am extremely nervous about getting this wrong, so I would like to see a lot of negative tests as well, for things that shouldn't work:

  • cloning from non-encrypted to encrypted datasets
  • cloning from encrypted to non-encrypted datasets
  • cloning from a snapshot dir to a filesystem that is not the source of that snapshot
  • cloning from a filesystem created from a snapshot clone to another unrelated dataset
    And so on. Probably some positive versions of these too.

@oromenahar
Copy link
Contributor Author

Good point for the test. Thanks for the suggestions, add it later.

module/zfs/dsl_crypt.c Outdated Show resolved Hide resolved
return (ret);
}

ret = spa_crypto_key_compare(dcka, dckb);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks that the datasets have the same dsl_crypto_key, which includes the wrapping key. I think that means two datasets with the same master key can have different dsl_crypto_key if one of them had its encryption root changed.

We could instead compare zk_guid which should match as long as the underlying master key is the same. I have a branch with this check, I'll clean it up and share. We should also add a test around this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6001dad switches the check to zk_guid and also cleans up the comparison logic according to other review comments.

I'm working on test cases. So far, I have found that performing a raw send into the same pool and then changing the key of one of the datasets is a reasonable way to end up with two different datasets with different encryption roots and the same master key.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Majiir I'm not sure actually. Changing the raw "master" key should be possible unless reencryption/changing the master key will be implemented. spa_crypto_key_compare uses the dck_obj wich should be unique for every master key. This represents the ID of ZAP object. I think this should be the same for a shared master key, shouldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh than the ZAP-ID could be different. Maybe you are right. Than spa_crypto_key_compare wouldn't return the truth, if you are right? Not sure, going to debug it in the next days. Thanks for the implementation and the commit.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The on-disk structure is the wrapping key, not the master key. It's possible in principle for the same master key to be stored in multiple wrapping keys. In those cases, we would expect dck_obj to differ but the master key (and zk_guid) to match.

In testing, I verified with the send | recv+change-key scenario that using spa_crypto_key_compare does not allow block cloning, while it works using the commit I linked above.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While building tests for this, I ran into a bug where ZFS crashes after send | recv+change-key+cp from another encrypted dataset. Crash log is here.

I still need to get the ZFS test suite running, but for now I have a reproducer in the form of a NixOS VM test. You don't need NixOS to run it, just the Nix package manager, which can run on your (Linux) distro of choice. To run it:

nix run github:Majiir/nixpkgs/35f0c1ad00cf4d72eeb7ffcc78e2e2a1037c6a07#nixosTests.zfs-block-cloning.stable.driver

You can find the test script here.

I tried applying #15543 and it didn't fix the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Majiir do you tested if the wrapping key is different when using send | recv + something....?
If this is so I would a hint to the original crypto_compare_function used a few minutes ago.

How did you crash this? Are you waiting for the sync before doing anything? Do you think it's related to this PR? Was sick last weekend and didn't had time to test/read your nix.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The crash no longer occurs on latest master (8ad73bf at time of writing) and block cloning works between the original filesystem and the send/recv'd one. 🎉

@oromenahar
Copy link
Contributor Author

I'm added a list the the first post which contains the missing tests and will later contains a tests wich are implemented. I'm going to update this list when adding a test or somebody suggest a test. I will push a new version of this, including an equal-function (@amotin suggestion), when I added tests, and the discussion with the documentation is finished. Until than I need a nap.

@behlendorf behlendorf added Status: Code Review Needed Ready for review and testing Component: Encryption "native encryption" feature labels Nov 27, 2023
man/man7/zpool-features.7 Outdated Show resolved Hide resolved
Copy link
Member

@amotin amotin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Just fix checkstyle and few comments.

module/zfs/dsl_crypt.c Outdated Show resolved Hide resolved
@oromenahar
Copy link
Contributor Author

I think we covered a lot of thoughts on three different PRs while working on encrypted cross clone. I prefer if this is merged to master. I want to create an issue to collect different possible test scenarios. I will than create a PR and update the new PR with the tests from time to time. Not all of the tests named above is written by now. But I want that people can see this issue and provide ideas. I would mange this a little bit.

All of this different scenarios must not be based on encrypted cross clone and wouldn't be super related to this PR. I covered some of the important scenarios with test. (hope so)
All of them are working as expected.

Do you think we can merge this @behlendorf and @amotin ?

@oromenahar
Copy link
Contributor Author

@robn can you please check the test block-cloning kshlib? I changed it to the cross-plattform md5digest as well, and this is your code.

man/man7/zpool-features.7 Outdated Show resolved Hide resolved
module/zfs/zfs_vnops.c Show resolved Hide resolved
When two datasets share the same master encryption key, it is safe
to clone encrypted blocks. Currently only snapshots and clones
of a dataset share with it the same encryption key.

Signed-off-by: Kay Pedersen <[email protected]>
Original-patched-by: Pawel Jakub Dawidek <[email protected]>
Added a test for:
- Clone from encrypted sibling to encrypted sibling with
  non encrypted parent
- Clone from encrypted parent to inherited encrypted child
- Clone from child to sibling with encrypted parent
- Clone from snapshot to the original datasets
- Clone from foreign snapshot to a foreign dataset

Signed-off-by: Kay Pedersen <[email protected]>
- cloning from non-encrypted to encrypted datasets
- cloning from encrypted to non-encrypted datasets

Signed-off-by: Kay Pedersen <[email protected]>
@behlendorf behlendorf added Status: Accepted Ready to integrate (reviewed, tested) and removed Status: Code Review Needed Ready for review and testing labels Dec 1, 2023
@behlendorf behlendorf merged commit c7b6119 into openzfs:master Dec 5, 2023
21 of 26 checks passed
@oromenahar oromenahar deleted the brt-crypt branch December 9, 2023 14:01
lundman pushed a commit to openzfsonwindows/openzfs that referenced this pull request Dec 12, 2023
When two datasets share the same master encryption key, it is safe
to clone encrypted blocks. Currently only snapshots and clones
of a dataset share with it the same encryption key.

Added a test for:
- Clone from encrypted sibling to encrypted sibling with
  non encrypted parent
- Clone from encrypted parent to inherited encrypted child
- Clone from child to sibling with encrypted parent
- Clone from snapshot to the original datasets
- Clone from foreign snapshot to a foreign dataset
- Cloning from non-encrypted to encrypted datasets
- Cloning from encrypted to non-encrypted datasets

Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Original-patch-by: Pawel Jakub Dawidek <[email protected]>
Signed-off-by: Kay Pedersen <[email protected]>
Closes openzfs#15544
mmatuska pushed a commit to mmatuska/zfs that referenced this pull request Dec 26, 2023
When two datasets share the same master encryption key, it is safe
to clone encrypted blocks. Currently only snapshots and clones
of a dataset share with it the same encryption key.

Added a test for:
- Clone from encrypted sibling to encrypted sibling with
  non encrypted parent
- Clone from encrypted parent to inherited encrypted child
- Clone from child to sibling with encrypted parent
- Clone from snapshot to the original datasets
- Clone from foreign snapshot to a foreign dataset
- Cloning from non-encrypted to encrypted datasets
- Cloning from encrypted to non-encrypted datasets

Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Original-patch-by: Pawel Jakub Dawidek <[email protected]>
Signed-off-by: Kay Pedersen <[email protected]>
Closes openzfs#15544
mmatuska pushed a commit to mmatuska/zfs that referenced this pull request Dec 27, 2023
When two datasets share the same master encryption key, it is safe
to clone encrypted blocks. Currently only snapshots and clones
of a dataset share with it the same encryption key.

Added a test for:
- Clone from encrypted sibling to encrypted sibling with
  non encrypted parent
- Clone from encrypted parent to inherited encrypted child
- Clone from child to sibling with encrypted parent
- Clone from snapshot to the original datasets
- Clone from foreign snapshot to a foreign dataset
- Cloning from non-encrypted to encrypted datasets
- Cloning from encrypted to non-encrypted datasets

Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Original-patch-by: Pawel Jakub Dawidek <[email protected]>
Signed-off-by: Kay Pedersen <[email protected]>
Closes openzfs#15544
mmatuska pushed a commit to mmatuska/zfs that referenced this pull request Dec 27, 2023
When two datasets share the same master encryption key, it is safe
to clone encrypted blocks. Currently only snapshots and clones
of a dataset share with it the same encryption key.

Added a test for:
- Clone from encrypted sibling to encrypted sibling with
  non encrypted parent
- Clone from encrypted parent to inherited encrypted child
- Clone from child to sibling with encrypted parent
- Clone from snapshot to the original datasets
- Clone from foreign snapshot to a foreign dataset
- Cloning from non-encrypted to encrypted datasets
- Cloning from encrypted to non-encrypted datasets

Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Original-patch-by: Pawel Jakub Dawidek <[email protected]>
Signed-off-by: Kay Pedersen <[email protected]>
Closes openzfs#15544
behlendorf pushed a commit that referenced this pull request Jan 9, 2024
When two datasets share the same master encryption key, it is safe
to clone encrypted blocks. Currently only snapshots and clones
of a dataset share with it the same encryption key.

Added a test for:
- Clone from encrypted sibling to encrypted sibling with
  non encrypted parent
- Clone from encrypted parent to inherited encrypted child
- Clone from child to sibling with encrypted parent
- Clone from snapshot to the original datasets
- Clone from foreign snapshot to a foreign dataset
- Cloning from non-encrypted to encrypted datasets
- Cloning from encrypted to non-encrypted datasets

Reviewed-by: Alexander Motin <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Original-patch-by: Pawel Jakub Dawidek <[email protected]>
Signed-off-by: Kay Pedersen <[email protected]>
Closes #15544
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Encryption "native encryption" feature Status: Accepted Ready to integrate (reviewed, tested)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants