-
Notifications
You must be signed in to change notification settings - Fork 594
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
overlord/devicestate,secboot: fix factory reset on older versions #14471
base: fde-manager-features
Are you sure you want to change the base?
overlord/devicestate,secboot: fix factory reset on older versions #14471
Conversation
e820f9a
to
21ed272
Compare
21ed272
to
4b3991d
Compare
fcc5e2e
to
24018ee
Compare
4b3991d
to
f463a03
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## fde-manager-features #14471 +/- ##
=======================================================
Coverage ? 77.67%
=======================================================
Files ? 1168
Lines ? 155564
Branches ? 0
=======================================================
Hits ? 120830
Misses ? 27239
Partials ? 7495
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
3fe46ac
to
29dd378
Compare
This might still need #14718 to work. We will see. |
c0efc73
to
d43620a
Compare
We are missing a piece, I have forgotten about that. We need to be able to convert LUKS2 disk without tokens to ones with tokens, just for the names, so we can rename and delete them. |
I have opened canonical/secboot#349 to have a way to write factory reset in a way that works. |
d43620a
to
bfbb339
Compare
This also depends on canonical/secboot#355 |
Draft, because it needs PRs to be merged on secboot. |
5802840
to
5ec2e43
Compare
bfbb339
to
f503fb1
Compare
f503fb1
to
288d176
Compare
288d176
to
7c2e31f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some questions/comments
secboot/secboot_sb.go
Outdated
@@ -348,7 +348,7 @@ func AddBootstrapKeyOnExistingDisk(node string, newKey keys.EncryptionKey) error | |||
// Rename key slots on LUKS2 container. If the key slot does not | |||
// exist, it is ignored. If cryptsetup does not support renaming, then | |||
// the key slots are instead removed. | |||
func RenameOrDeleteKeys(node string, renames map[string]string) error { | |||
func RenameKeys(nonAtomic *NonAtomicOperationAllowedFlag, node string, renames map[string]string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are there calls to RenameKeys that will not pass nonAtomic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point we have only one call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then the nonAtomic bit here is probably overkill, we can just have a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I will simplify it then.
if err := secbootDeleteKeys(diskPath, toDelete); err != nil { | ||
return fmt.Errorf("cannot delete previous keys: %w", err) | ||
} | ||
if err := secbootRemoveOldDiskKeys(diskPath); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it a bit strange that we both call DeleteKeys and then call a helper that does a bit of that as well, at a minimum these need comments about what each does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is also difficult to explain the name of the function. One remove old keys if you reset from a new version. The other one is if you reset from an old version where snapd was updated in the seed with a remodel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added comments before the calls to explain what both are doing.
secboot/secboot_sb.go
Outdated
@@ -348,7 +348,7 @@ func AddBootstrapKeyOnExistingDisk(node string, newKey keys.EncryptionKey) error | |||
// Rename key slots on LUKS2 container. If the key slot does not | |||
// exist, it is ignored. If cryptsetup does not support renaming, then | |||
// the key slots are instead removed. | |||
func RenameOrDeleteKeys(node string, renames map[string]string) error { | |||
func RenameKeys(nonAtomic *NonAtomicOperationAllowedFlag, node string, renames map[string]string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a very weird API tbh. The call sites where this isn't passed will see a bare nil
as the first argument which is even more confusing than bare true/false. I understand canonical/secboot does this, but it doesn't make it any less weird and it doesn't need to be exposed outside of the secboot package.
If this needs an argument then pass it in a struct:
type RenameKeysFlags struct {
AllowNonAtomic bool
}
func RenameKeys(node string, renames map[string]string, flags RenameKeysFlags) error {
..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is on purpose weird because should avoid using this mode. Chris was concerned that would get used when we do not need to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As requested by @pedronis this was simplified.
7005c64
to
25f9119
Compare
91da819
to
1e6820c
Compare
Tue Feb 4 00:34:40 UTC 2025 Failures:Preparing:
Executing:
Restoring:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, this looks good, a bunch of naming/commenting remarks.
Can you remind what happens with all the rename if the factory reset doesn't quite finish?
secboot/secboot_sb.go
Outdated
// ConvertOldDisk takes on disk using legacy keyslots 0, 1, 2 and add | ||
// names to those keyslots. This is needed to convert the save disk | ||
// during a factory reset. | ||
func ConvertOldDisk(devicePath string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Convert/TemporaryNameOldKeys/ would probably describe better what this does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this should mention that it is for migrating during factory-reset and that it is a noop if not unamed legacy keyslots exists.
secboot/secboot_sb.go
Outdated
@@ -348,7 +348,8 @@ func AddBootstrapKeyOnExistingDisk(node string, newKey keys.EncryptionKey) error | |||
// Rename key slots on LUKS2 container. If the key slot does not | |||
// exist, it is ignored. If cryptsetup does not support renaming, then | |||
// the key slots are instead removed. | |||
func RenameOrDeleteKeys(node string, renames map[string]string) error { | |||
// WARNING: this function is not atomic. Please avoid using it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is not always atomic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, it is not atomic only if cryptsetup is too old. I will amend that.
secboot/secboot_sb.go
Outdated
|
||
// RemoveOldDiskKeys removes key slots from an old installation that | ||
// had names created by ConvertOldDisk. | ||
func RemoveOldDiskKeys(devicePath string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't DeleteOldKeys be more consistent with other naming here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this will not fail if the key don't exist right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the keys of an old disk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DeleteOldKeys is fine, I will change to it.
return nil, fmt.Errorf("cannot rename existing keys: %w", err) | ||
} | ||
|
||
if err := secbootConvertOldDisk(saveNode); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// deal as needed instead with naming unamed keyslots, they will be removed at the end of factory reset
5750e24
to
b835a09
Compare
For the preparation of the factory reset, if the preparation is not finished, the keys are already named or renamed. If the factory reset clean up does not finish, it should run again. Because we have a flag that needs to be removed last. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
secboot/secboot_sb.go
Outdated
// old, it will try to copy and delete keys instead. Please avoid | ||
// using this function. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// old, it will try to copy and delete keys instead. Please avoid | |
// using this function. | |
// old, it will try to copy and delete keys instead. Avoid | |
// using this function in new code. |
secboot/secboot_sb.go
Outdated
// add names to those keyslots. This is needed to convert the save | ||
// disk during a factory reset. This is a no-operation if no keyslot | ||
// are unnamed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// add names to those keyslots. This is needed to convert the save | |
// disk during a factory reset. This is a no-operation if no keyslot | |
// are unnamed. | |
// adds names to those keyslots. This is needed to convert the save | |
// disk during a factory reset. This is a no-operation if keyslots | |
// are already named. |
|
||
prepare: | | ||
# Install what is needed before using the fake store | ||
snap install jq remarshal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't be needed anymore, nested.sh was updated to use gojq
snap install jq remarshal |
b835a09
to
9a27837
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks mostly ok - I've left a few comments, including a suggestion for TemporaryNameOldKeys
.
I know we're trying to deliver a MVP for this cycle, but it feels a bit like factory-reset and reprovision are getting tangled together quite a bit. I just want to keep in mind that as per FO166, reprovision should be able to operate independently of factory reset eventually, and it will be required to do so for the post-recovery repair functionality in any case.
return nil, err | ||
// Temporarily rename keyslots across the factory reset to | ||
// allow to create the new ones. | ||
if err := secbootRenameKeys(saveNode, renames); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FO166, which describes reprovisioning for FDE, suggests renaming existing keyslots early on to reprovision-XX
(with XX
being a numeric identifier).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can change that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure though what "persist the mapping of temporary to old key slot names" means this case. I wonder if maybe we can add a FIXME here and change the names once we have this persisting of the mapping.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could prefix with "reprovision-" but keep the name instead of using an id for now.
// disk during a factory reset. This is a no-operation if all keyslots | ||
// are already named. | ||
func TemporaryNameOldKeys(devicePath string) error { | ||
if err := sb.NameLegacyLUKS2ContainerKey(devicePath, 0, "old-default-key"); err != nil && !errors.Is(err, sb.KeyslotAlreadyHasANameErr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this function is making assumption that the container structure is unmodified from the original install, I think it could be improved a bit to make it safer.
If the error is secboot.KeyslotAlreadyHasANameErr
, should you check that the keyslot has the expected name before ignoring the error? I think you can probably do that by listing all names with secboot.ListLUKS2ContainerUnlockKeyNames
, and then creating a secboot.LUKSKeyDataReader
from each name and calling the KeyslotID
method to verify that no unexpected name points to each of the keyslots 0-2.
Or an alternative way might be to turn secboot.KeyslotAlreadyHasANameErr
into a structure that contains the keyslot's existing name, or define the type of the error based on a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer doing the removal of other keys that are named out of this function. They are named, so it is part of normal factory reset.
secboot/secboot_tpm.go
Outdated
@@ -837,7 +837,7 @@ func PCRHandleOfSealedKey(p string) (uint32, error) { | |||
func tpmReleaseResourcesImpl(tpm *sb_tpm2.Connection, handle tpm2.Handle) error { | |||
rc, err := tpm.CreateResourceContextFromTPM(handle) | |||
if err != nil { | |||
if _, ok := err.(tpm2.ResourceUnavailableError); ok { | |||
if errors.Is(err, &tpm2.ResourceUnavailableError{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is also tpm2.IsResourceUnavailableError
9a27837
to
9b8bfaf
Compare
Until now, we were just delete old keys on factory-reset instead of renaming if cryptsetup was too old. But this is a bit too dangerous. Instead we copy-delete them. Second, we need to convert old keyslots to have a name so we can remove them after.
9b8bfaf
to
d784faa
Compare
Until now, we were just delete old keys on factory-reset instead of
renaming if cryptsetup was too old. But this is a bit too dangerous.
Instead we copy-delete them.
Second, we need to convert old keyslots to have a name so we can
remove them after.