-
Notifications
You must be signed in to change notification settings - Fork 185
vaultmgr: recover a wrong vault key mode #6451
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
Merged
eriknordmark
merged 13 commits into
lf-edge:master
from
eriknordmark:vault-mode-recovery
Sep 26, 2026
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
15d51f5
vault: drop the staging zvol on a failed migration
eriknordmark 93230c0
vault: never promote a partial migration copy
eriknordmark 56ab5f8
docs: vault migration leftovers and recovery
eriknordmark 7fd554d
vault: fault point for an interrupted migration swap
eriknordmark 497421a
vault: keep migration datasets beside a foreign vault
eriknordmark 6d53121
vault: refuse to migrate a vault the backup did not come from
eriknordmark 473cbd2
vault: drop the migration fallback once the partition commits
eriknordmark 4477e2d
vault: recover a cut migration on EVE-kvm
eriknordmark 8be89b2
vault: read partition state without a TPM too
eriknordmark 6a230c1
vault: drop the etcd zvol a failed migration made
eriknordmark a486f6d
vaultmgr: recover a wrong vault key mode
eriknordmark 88049c5
vaultmgr: unit-test the vault key mode decisions
eriknordmark 1000210
evetest: cover recovery of a wrong vault key mode
eriknordmark File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // Copyright (c) 2026 Zededa, Inc. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package vaultmgr | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/lf-edge/eve/pkg/pillar/base" | ||
| "github.com/lf-edge/eve/pkg/pillar/pubsub" | ||
| "github.com/lf-edge/eve/pkg/pillar/types" | ||
| "github.com/lf-edge/eve/pkg/pillar/vault" | ||
| "github.com/sirupsen/logrus" | ||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| // initTest points the package logger at the test logger and restores the | ||
| // package-level vault config the functions under test read, so the order the | ||
| // tests run in cannot matter. | ||
| func initTest(t *testing.T) { | ||
| log = base.NewSourceLogObject(logrus.StandardLogger(), "test", 0) | ||
| config, inited := vaultConfig, vaultConfigInited | ||
| t.Cleanup(func() { | ||
| vaultConfig, vaultConfigInited = config, inited | ||
| }) | ||
| } | ||
|
|
||
| // A recorded mode is where unlocking starts. It is not treated as a fact -- | ||
| // the handler falls back to the other derivation -- but it is what gets tried | ||
| // first, so a device that has one never pays for a wrong first guess. | ||
| func TestVaultKeyModeUsesThePersistedConfig(t *testing.T) { | ||
| initTest(t) | ||
| for _, tpmKeyOnly := range []bool{true, false} { | ||
| vaultConfig = types.VaultConfig{TpmKeyOnly: tpmKeyOnly} | ||
| vaultConfigInited = true | ||
| assert.Equal(t, tpmKeyOnly, vaultKeyMode()) | ||
| } | ||
| } | ||
|
|
||
| // With no recorded mode, the derivation a vault created today is keyed with is | ||
| // the one to try first. It is wrong for a pre-7.10.0 merged-key vault whose | ||
| // config was lost, which is exactly what the unlock fallback covers. | ||
| func TestVaultKeyModeWithoutAPersistedConfig(t *testing.T) { | ||
| initTest(t) | ||
| // A value the persisted-config branch would return, so dropping the branch | ||
| // check below shows up as the wrong answer rather than as a coincidence. | ||
| vaultConfig = types.VaultConfig{TpmKeyOnly: false} | ||
| vaultConfigInited = false | ||
| assert.True(t, vaultKeyMode()) | ||
| } | ||
|
|
||
| func TestKeyDerivationOf(t *testing.T) { | ||
| assert.Equal(t, types.VaultKeyDerivationTPMOnly, keyDerivationOf(true)) | ||
| assert.Equal(t, types.VaultKeyDerivationTPMAndConstant, keyDerivationOf(false)) | ||
| } | ||
|
|
||
| // fakeHandler reports a resolved key mode and nothing else; recordVaultKeyMode | ||
| // reaches no other part of the interface. | ||
| type fakeHandler struct { | ||
| vault.Handler | ||
| options vault.HandlerOptions | ||
| } | ||
|
|
||
| func (h fakeHandler) GetHandlerOptions() vault.HandlerOptions { | ||
| return h.options | ||
| } | ||
|
|
||
| func setHandler(t *testing.T, h vault.Handler) { | ||
| previous := handler | ||
| handler = h | ||
| t.Cleanup(func() { handler = previous }) | ||
| } | ||
|
|
||
| // newKeyModeCtx returns a context whose VaultConfig publication is in memory, | ||
| // so what recordVaultKeyMode writes can be read back. | ||
| func newKeyModeCtx(t *testing.T, tpmEnabled bool) *vaultMgrContext { | ||
| t.Helper() | ||
| ps := pubsub.New(pubsub.NewMemoryDriver(), logrus.StandardLogger(), log) | ||
| pub, err := ps.NewPublication(pubsub.PublicationOptions{ | ||
| AgentName: agentName, | ||
| TopicType: types.VaultConfig{}, | ||
| }) | ||
| if err != nil { | ||
| t.Fatalf("NewPublication: %v", err) | ||
| } | ||
| return &vaultMgrContext{pubVaultConfig: pub, tpmEnabled: tpmEnabled} | ||
| } | ||
|
|
||
| func recordedKeyMode(t *testing.T, ctx *vaultMgrContext) (types.VaultConfig, bool) { | ||
| t.Helper() | ||
| item, err := ctx.pubVaultConfig.Get(types.VaultConfig{}.Key()) | ||
| if err != nil { | ||
| return types.VaultConfig{}, false | ||
| } | ||
| config, ok := item.(types.VaultConfig) | ||
| assert.True(t, ok, "VaultConfig publication holds %T", item) | ||
| return config, true | ||
| } | ||
|
|
||
| // What gets recorded is the derivation that opened the vault, read off the | ||
| // handler. Recording the first guess instead is what made a lost mode | ||
| // permanent. | ||
| func TestRecordVaultKeyModeRecordsTheResolvedMode(t *testing.T) { | ||
| initTest(t) | ||
| setHandler(t, fakeHandler{options: vault.HandlerOptions{TpmKeyOnlyMode: true}}) | ||
| vaultConfigInited = false | ||
| ctx := newKeyModeCtx(t, true) | ||
|
|
||
| recordVaultKeyMode(ctx) | ||
|
|
||
| config, recorded := recordedKeyMode(t, ctx) | ||
| assert.True(t, recorded) | ||
| assert.True(t, config.TpmKeyOnly) | ||
| assert.True(t, vaultConfigInited) | ||
| } | ||
|
|
||
| // A recorded mode the vault no longer uses has to be overwritten. The vault | ||
| // recreate path is where the two come apart: it destroys the old vault and | ||
| // keys the replacement TPM-key-only, leaving whatever was recorded describing | ||
| // a vault that is gone. A boot that then trusted the record would start from | ||
| // the wrong derivation. | ||
| func TestRecordVaultKeyModeOverwritesAStaleMode(t *testing.T) { | ||
| initTest(t) | ||
| setHandler(t, fakeHandler{options: vault.HandlerOptions{TpmKeyOnlyMode: false}}) | ||
| vaultConfigInited = false | ||
| ctx := newKeyModeCtx(t, true) | ||
| recordVaultKeyMode(ctx) | ||
|
|
||
| setHandler(t, fakeHandler{options: vault.HandlerOptions{TpmKeyOnlyMode: true}}) | ||
| recordVaultKeyMode(ctx) | ||
|
|
||
| config, _ := recordedKeyMode(t, ctx) | ||
| assert.True(t, config.TpmKeyOnly) | ||
| } | ||
|
|
||
| // Without a TPM the vault key is not derived from one, so there is no mode to | ||
| // record -- and none may be left behind for a later boot to read as its own. | ||
| func TestRecordVaultKeyModeSkipsWithoutTpm(t *testing.T) { | ||
| initTest(t) | ||
| setHandler(t, fakeHandler{options: vault.HandlerOptions{TpmKeyOnlyMode: true}}) | ||
| vaultConfigInited = false | ||
| ctx := newKeyModeCtx(t, false) | ||
|
|
||
| recordVaultKeyMode(ctx) | ||
|
|
||
| _, recorded := recordedKeyMode(t, ctx) | ||
| assert.False(t, recorded) | ||
| assert.False(t, vaultConfigInited) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.