Skip to content

Commit

Permalink
many: add target install system checks for passphrase support (canoni…
Browse files Browse the repository at this point in the history
…cal#14943)

* many: add target install system checks for passphrase support

Signed-off-by: Zeyad Gouda <zeyad.gouda@canonical.com>

* fixup! o/{install,devicestate}: use a struct instead of map for snapd versions

Signed-off-by: Zeyad Gouda <zeyad.gouda@canonical.com>

* fixup! many: compute snapd versions in loadSystemAndEssentialSnaps

Signed-off-by: Zeyad Gouda <zeyad.gouda@canonical.com>

---------

Signed-off-by: Zeyad Gouda <zeyad.gouda@canonical.com>
  • Loading branch information
ZeyadYasser authored Feb 11, 2025
1 parent c4ff9e7 commit 84e9ddc
Show file tree
Hide file tree
Showing 10 changed files with 421 additions and 93 deletions.
13 changes: 12 additions & 1 deletion daemon/api_systems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (s *systemsSuite) mockSystemSeeds(c *check.C) (restore func()) {

assertstest.AddMany(s.StoreSigning.Database, s.Brands.AccountsAndKeys("my-brand")...)
// add essential snaps
seed20.MakeAssertedSnap(c, "name: snapd\nversion: 1\ntype: snapd", nil, snap.R(1), "my-brand", s.StoreSigning.Database)
seed20.MakeAssertedSnap(c, "name: snapd\nversion: 1\ntype: snapd", [][]string{{"usr/lib/snapd/info", "VERSION=1"}}, snap.R(1), "my-brand", s.StoreSigning.Database)
gadgetFiles := [][]string{
{"meta/gadget.yaml", string(pcGadgetUCYaml)},
{"pc-boot.img", "pc-boot.img content"},
Expand All @@ -152,6 +152,11 @@ func (s *systemsSuite) mockSystemSeeds(c *check.C) (restore func()) {
"architecture": "amd64",
"base": "core20",
"snaps": []interface{}{
map[string]interface{}{
"name": "snapd",
"id": seed20.AssertedSnapID("snapd"),
"type": "snapd",
},
map[string]interface{}{
"name": "pc-kernel",
"id": seed20.AssertedSnapID("pc-kernel"),
Expand All @@ -170,6 +175,11 @@ func (s *systemsSuite) mockSystemSeeds(c *check.C) (restore func()) {
"architecture": "amd64",
"base": "core20",
"snaps": []interface{}{
map[string]interface{}{
"name": "snapd",
"id": seed20.AssertedSnapID("snapd"),
"type": "snapd",
},
map[string]interface{}{
"name": "pc-kernel",
"id": seed20.AssertedSnapID("pc-kernel"),
Expand Down Expand Up @@ -978,6 +988,7 @@ func (s *systemsSuite) TestSystemsGetSpecificLabelIntegration(c *check.C) {
// mockSystemSeed will ensure everything here is coming from
// the mocked seed except the encryptionInfo
sys, gadgetInfo, encInfo, err := deviceMgr.SystemAndGadgetAndEncryptionInfo(label)
c.Assert(err, check.IsNil)
// encryptionInfo needs get overridden here to get reliable tests
encInfo.Available = false
encInfo.StorageSafety = asserts.StorageSafetyPreferEncrypted
Expand Down
41 changes: 28 additions & 13 deletions overlord/devicestate/devicemgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -2202,7 +2202,7 @@ func (m *DeviceManager) SystemAndGadgetAndEncryptionInfo(wantedSystemLabel strin
// installer is not anymore.

// System information
systemAndSnaps, err := m.loadSystemAndEssentialSnaps(wantedSystemLabel, []snap.Type{snap.TypeKernel, snap.TypeGadget}, seed.AllModes)
systemAndSnaps, err := m.loadSystemAndEssentialSnaps(wantedSystemLabel, []snap.Type{snap.TypeSnapd, snap.TypeKernel, snap.TypeGadget}, seed.AllModes)
if err != nil {
return nil, nil, nil, err
}
Expand All @@ -2218,7 +2218,7 @@ func (m *DeviceManager) SystemAndGadgetAndEncryptionInfo(wantedSystemLabel strin
}

// Encryption details
encInfo, err := m.encryptionSupportInfo(systemAndSnaps.Model, secboot.TPMProvisionFull, systemAndSnaps.InfosByType[snap.TypeKernel], gadgetInfo)
encInfo, err := m.encryptionSupportInfo(systemAndSnaps.Model, secboot.TPMProvisionFull, systemAndSnaps.InfosByType[snap.TypeKernel], gadgetInfo, &systemAndSnaps.SystemSnapdVersions)
if err != nil {
return nil, nil, nil, err
}
Expand All @@ -2236,10 +2236,11 @@ func (m *DeviceManager) SystemAndGadgetAndEncryptionInfo(wantedSystemLabel strin

type systemAndEssentialSnaps struct {
*System
Seed seed.Seed
InfosByType map[snap.Type]*snap.Info
CompsByType map[snap.Type][]install.ComponentSeedInfo
SeedSnapsByType map[snap.Type]*seed.Snap
Seed seed.Seed
SystemSnapdVersions install.SystemSnapdVersions
InfosByType map[snap.Type]*snap.Info
CompsByType map[snap.Type][]install.ComponentSeedInfo
SeedSnapsByType map[snap.Type]*seed.Snap
}

// DefaultRecoverySystem returns the default recovery system, if there is one.
Expand Down Expand Up @@ -2297,6 +2298,7 @@ func (m *DeviceManager) loadSystemAndEssentialSnaps(wantedSystemLabel string, ty
snapInfos := make(map[snap.Type]*snap.Info)
compInfos := make(map[snap.Type][]install.ComponentSeedInfo)
seedSnaps := make(map[snap.Type]*seed.Snap)
systemSnapdVersions := install.SystemSnapdVersions{}
for _, seedSnap := range s.EssentialSnaps() {
typ := seedSnap.EssentialType
if seedSnap.Path == "" {
Expand Down Expand Up @@ -2339,6 +2341,18 @@ func (m *DeviceManager) loadSystemAndEssentialSnaps(wantedSystemLabel string, ty
})
}
}
if typ == snap.TypeSnapd || typ == snap.TypeKernel {
snapdVersion, _, err := snap.SnapdInfoFromSnapFile(snapf, typ)
if err != nil {
return nil, err
}
switch typ {
case snap.TypeSnapd:
systemSnapdVersions.SnapdVersion = snapdVersion
case snap.TypeKernel:
systemSnapdVersions.SnapdInitramfsVersion = snapdVersion
}
}
seedSnaps[typ] = snapForMode
snapInfos[typ] = snapInfo
compInfos[typ] = compInfosForType
Expand All @@ -2348,11 +2362,12 @@ func (m *DeviceManager) loadSystemAndEssentialSnaps(wantedSystemLabel string, ty
}

return &systemAndEssentialSnaps{
System: sys,
Seed: s,
InfosByType: snapInfos,
CompsByType: compInfos,
SeedSnapsByType: seedSnaps,
System: sys,
Seed: s,
SystemSnapdVersions: systemSnapdVersions,
InfosByType: snapInfos,
CompsByType: compInfos,
SeedSnapsByType: seedSnaps,
}, nil
}

Expand Down Expand Up @@ -2856,6 +2871,6 @@ func (m *DeviceManager) checkEncryption(st *state.State, deviceCtx snapstate.Dev
return install.CheckEncryptionSupport(model, tpmMode, kernelInfo, gadgetInfo, m.runFDESetupHook)
}

func (m *DeviceManager) encryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvisionMode, kernelInfo *snap.Info, gadgetInfo *gadget.Info) (install.EncryptionSupportInfo, error) {
return install.GetEncryptionSupportInfo(model, tpmMode, kernelInfo, gadgetInfo, m.runFDESetupHook)
func (m *DeviceManager) encryptionSupportInfo(model *asserts.Model, tpmMode secboot.TPMProvisionMode, kernelInfo *snap.Info, gadgetInfo *gadget.Info, systemSnapdVersions *install.SystemSnapdVersions) (install.EncryptionSupportInfo, error) {
return install.GetEncryptionSupportInfo(model, tpmMode, kernelInfo, gadgetInfo, systemSnapdVersions, m.runFDESetupHook)
}
98 changes: 93 additions & 5 deletions overlord/devicestate/devicestate_install_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,11 +765,26 @@ func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryption(c *C, hasTP
seedCopyFn := func(seedDir string, opts seed.CopyOptions, tm timings.Measurer) error {
return fmt.Errorf("unexpected copy call")
}
var snapdVersionByType map[snap.Type]string
if withVolumesAuth {
// Passphrase auth requires snapd 2.68 as a minimum in target install system
snapdVersionByType = map[snap.Type]string{
snap.TypeSnapd: "2.68",
snap.TypeKernel: "2.68",
}
} else {
// mock other versions to cover more cases
snapdVersionByType = map[snap.Type]string{
snap.TypeSnapd: "2.67",
snap.TypeKernel: "2.66",
}
}
seedOpts := mockSystemSeedWithLabelOpts{
isClassic: isClassic,
hasSystemSeed: false,
hasPartial: false,
types: []snap.Type{snap.TypeKernel, snap.TypeBase, snap.TypeGadget},
isClassic: isClassic,
hasSystemSeed: false,
hasPartial: false,
types: []snap.Type{snap.TypeSnapd, snap.TypeKernel, snap.TypeBase, snap.TypeGadget},
snapdVersionByType: snapdVersionByType,
}
gadgetSnapPath, kernelSnapPath, _, ginfo, mountCmd, _ := s.mockSystemSeedWithLabel(
c, label, seedCopyFn, seedOpts)
Expand Down Expand Up @@ -846,7 +861,7 @@ func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryption(c *C, hasTP
return
}

c.Check(chg.Err(), IsNil)
c.Assert(chg.Err(), IsNil)
gadgetDir := filepath.Join(dirs.SnapRunDir, "snap-content/gadget")
kernelDir := filepath.Join(dirs.SnapRunDir, "snap-content/kernel")
c.Check(mountCmd.Calls(), DeepEquals, [][]string{
Expand Down Expand Up @@ -1004,3 +1019,76 @@ func (s *deviceMgrInstallAPISuite) TestInstallSetupStorageEncryptionBadVolumesAu
// Cached auth options are cleaned
c.Check(s.state.Cached(devicestate.VolumesAuthOptionsKeyByLabel(label)), IsNil)
}

func (s *deviceMgrInstallAPISuite) testInstallSetupStorageEncryptionPassphraseAuthUnsupportedSnap(c *C, snapdVersionByType map[snap.Type]string) {
// Mock label
label := "classic"
seedCopyFn := func(seedDir string, opts seed.CopyOptions, tm timings.Measurer) error {
return fmt.Errorf("unexpected copy call")
}
seedOpts := mockSystemSeedWithLabelOpts{
isClassic: true,
hasSystemSeed: false,
hasPartial: false,
types: []snap.Type{snap.TypeSnapd, snap.TypeKernel, snap.TypeBase, snap.TypeGadget},
snapdVersionByType: snapdVersionByType,
}

_, _, _, ginfo, _, _ := s.mockSystemSeedWithLabel(c, label, seedCopyFn, seedOpts)

s.state.Lock()
defer s.state.Unlock()

// Simulate system with TPM
restore := installLogic.MockSecbootCheckTPMKeySealingSupported(func(tpmMode secboot.TPMProvisionMode) error {
c.Check(tpmMode, Equals, secboot.TPMProvisionFull)
return nil
})
s.AddCleanup(restore)

restore = devicestate.MockInstallEncryptPartitions(func(onVolumes map[string]*gadget.Volume, volumesAuth *device.VolumesAuthOptions, encryptionType device.EncryptionType, model *asserts.Model, gadgetRoot, kernelRoot string, perfTimings timings.Measurer) (*install.EncryptionSetupData, error) {
return &install.EncryptionSetupData{}, nil
})
s.AddCleanup(restore)

// Create change
chg := s.state.NewChange("install-step-setup-storage-encryption",
"Setup storage encryption")
encryptTask := s.state.NewTask("install-setup-storage-encryption",
"install API set-up encryption step")
encryptTask.Set("system-label", label)
encryptTask.Set("on-volumes", ginfo.Volumes)
encryptTask.Set("volumes-auth-required", true)
mockVolumesAuth := &device.VolumesAuthOptions{Mode: device.AuthModePassphrase, Passphrase: "1234"}
s.state.Cache(devicestate.VolumesAuthOptionsKeyByLabel(label), mockVolumesAuth)
chg.AddTask(encryptTask)

// now let the change run - some checks will happen in the mocked functions
s.state.Unlock()
defer s.state.Lock()

s.settle(c)

s.state.Lock()
defer s.state.Unlock()

// Checks now
c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:
- install API set-up encryption step \(\"passphrase\" authentication mode is not supported by target system\)`)
}

func (s *deviceMgrInstallAPISuite) TestInstallSetupStorageEncryptionPassphraseAuthUnsupportedSnapd(c *C) {
snapdVersionByType := map[snap.Type]string{
snap.TypeSnapd: "2.67",
snap.TypeKernel: "2.68",
}
s.testInstallSetupStorageEncryptionPassphraseAuthUnsupportedSnap(c, snapdVersionByType)
}

func (s *deviceMgrInstallAPISuite) TestInstallSetupStorageEncryptionPassphraseAuthUnsupportedKernel(c *C) {
snapdVersionByType := map[snap.Type]string{
snap.TypeSnapd: "2.68",
snap.TypeKernel: "2.67",
}
s.testInstallSetupStorageEncryptionPassphraseAuthUnsupportedSnap(c, snapdVersionByType)
}
63 changes: 45 additions & 18 deletions overlord/devicestate/devicestate_install_mode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (s *deviceMgrInstallSuite) SetUpTest(c *C) {
s.SeedDir = dirs.SnapSeedDir
}

func (s *deviceMgrInstallSuite) setupSystemSeed(c *C, sysLabel, gadgetYaml string, isClassic bool, kModsRevs map[string]snap.Revision) *asserts.Model {
func (s *deviceMgrInstallSuite) setupSystemSeed(c *C, sysLabel, gadgetYaml string, isClassic bool, kModsRevs map[string]snap.Revision, snapdVersionByType map[snap.Type]string) *asserts.Model {
s.StoreSigning = assertstest.NewStoreStack("can0nical", nil)

s.Brands = assertstest.NewSigningAccounts(s.StoreSigning)
Expand All @@ -101,21 +101,25 @@ func (s *deviceMgrInstallSuite) setupSystemSeed(c *C, sysLabel, gadgetYaml strin

assertstest.AddMany(s.StoreSigning.Database, s.Brands.AccountsAndKeys("my-brand")...)

s.MakeAssertedSnap(c, seedtest.SampleSnapYaml["snapd"], nil, snap.R(1), "my-brand", s.StoreSigning.Database)
s.MakeAssertedSnap(c,
seedtest.SampleSnapYaml["snapd"],
[][]string{{"usr/lib/snapd/info", fmt.Sprintf("VERSION=%s", snapdVersionByType[snap.TypeSnapd])}},
snap.R(1), "my-brand", s.StoreSigning.Database)
s.MakeAssertedSnap(c, seedtest.SampleSnapYaml["core24"], nil, snap.R(1), "my-brand", s.StoreSigning.Database)
s.MakeAssertedSnap(c, seedtest.SampleSnapYaml["pc=24"],
[][]string{
{"meta/gadget.yaml", gadgetYaml},
{"pc-boot.img", ""}, {"pc-core.img", ""}, {"grubx64.efi", ""},
{"shim.efi.signed", ""}, {"grub.conf", ""}},
snap.R(1), "my-brand", s.StoreSigning.Database)
kernelFiles := [][]string{{"kernel.efi", ""}}
if _, ok := snapdVersionByType[snap.TypeKernel]; ok {
kernelFiles = append(kernelFiles, []string{"snapd-info", fmt.Sprintf("VERSION=%s", snapdVersionByType[snap.TypeKernel])})
}
if len(kModsRevs) > 0 {
s.MakeAssertedSnapWithComps(c,
seedtest.SampleSnapYaml["pc-kernel=24+kmods"],
[][]string{{"kernel.efi", ""}}, snap.R(1), kModsRevs, "my-brand", s.StoreSigning.Database)
s.MakeAssertedSnapWithComps(c, seedtest.SampleSnapYaml["pc-kernel=24+kmods"], kernelFiles, snap.R(1), kModsRevs, "my-brand", s.StoreSigning.Database)
} else {
s.MakeAssertedSnap(c, seedtest.SampleSnapYaml["pc-kernel=24"],
[][]string{{"kernel.efi", ""}}, snap.R(1), "my-brand", s.StoreSigning.Database)
s.MakeAssertedSnap(c, seedtest.SampleSnapYaml["pc-kernel=24"], kernelFiles, snap.R(1), "my-brand", s.StoreSigning.Database)
}

s.MakeAssertedSnapWithComps(c, seedtest.SampleSnapYaml["optional24"], nil, snap.R(1), nil, "my-brand", s.StoreSigning.Database)
Expand Down Expand Up @@ -193,13 +197,14 @@ func (s *fakeSeedCopier) OptionalContainers() (seed.OptionalContainers, error) {
}

type mockSystemSeedWithLabelOpts struct {
isClassic bool
hasSystemSeed bool
hasPartial bool
preseedArtifact bool
testCompsMode bool
kModsRevs map[string]snap.Revision
types []snap.Type
isClassic bool
hasSystemSeed bool
hasPartial bool
preseedArtifact bool
testCompsMode bool
kModsRevs map[string]snap.Revision
types []snap.Type
snapdVersionByType map[snap.Type]string
}

func (s *deviceMgrInstallSuite) mockSystemSeedWithLabel(c *C, label string, seedCopyFn func(string, seed.CopyOptions, timings.Measurer) error, opts mockSystemSeedWithLabelOpts) (gadgetSnapPath, kernelSnapPath string, kCompsPaths []string, ginfo *gadget.Info, mountCmd *testutil.MockCmd, model *asserts.Model) {
Expand All @@ -226,11 +231,12 @@ func (s *deviceMgrInstallSuite) mockSystemSeedWithLabel(c *C, label string, seed
s.AddCleanup(restore)

// now create a label with snaps/assertions
model = s.setupSystemSeed(c, label, seedGadget, opts.isClassic, opts.kModsRevs)
model = s.setupSystemSeed(c, label, seedGadget, opts.isClassic, opts.kModsRevs, opts.snapdVersionByType)
c.Check(model, NotNil)

// Create fake seed that will return information from the label we created
// (TODO: needs to be in sync with setupSystemSeed, fix that)
snapdSnapPath := filepath.Join(s.SeedDir, "snaps", "snapd_1.snap")
kernelSnapPath = filepath.Join(s.SeedDir, "snaps", "pc-kernel_1.snap")
baseSnapPath := filepath.Join(s.SeedDir, "snaps", "core24_1.snap")
gadgetSnapPath = filepath.Join(s.SeedDir, "snaps", "pc_1.snap")
Expand Down Expand Up @@ -258,6 +264,13 @@ func (s *deviceMgrInstallSuite) mockSystemSeedWithLabel(c *C, label string, seed
essentialSnaps := make([]*seed.Snap, 0, len(opts.types))
for _, typ := range opts.types {
switch typ {
case snap.TypeSnapd:
essentialSnaps = append(essentialSnaps, &seed.Snap{
Path: snapdSnapPath,
SideInfo: &snap.SideInfo{RealName: "snapd",
Revision: snap.R(1), SnapID: s.SeedSnaps.AssertedSnapID("snapd")},
EssentialType: snap.TypeSnapd,
})
case snap.TypeKernel:
essentialSnaps = append(essentialSnaps, &seed.Snap{
Path: kernelSnapPath,
Expand Down Expand Up @@ -1242,6 +1255,7 @@ func (s *deviceMgrInstallModeSuite) TestInstallRestoresPreseedArtifactModelMisma
type fakeSeed struct {
sysDir string
essentialSnaps []*seed.Snap
loadedSnaps []*seed.Snap
model *asserts.Model
preseedArtifact bool
}
Expand Down Expand Up @@ -1270,15 +1284,28 @@ func (*fakeSeed) Brand() (*asserts.Account, error) {
return nil, nil
}

func (*fakeSeed) LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error {
func (fs *fakeSeed) LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error {
if len(essentialTypes) == 0 {
fs.loadedSnaps = nil
}
fs.loadedSnaps = make([]*seed.Snap, 0, len(essentialTypes))
for _, typ := range essentialTypes {
for _, seedSnap := range fs.essentialSnaps {
if seedSnap.EssentialType == typ {
fs.loadedSnaps = append(fs.loadedSnaps, seedSnap)
break
}
}
}
return nil
}

func (*fakeSeed) LoadEssentialMetaWithSnapHandler([]snap.Type, seed.ContainerHandler, timings.Measurer) error {
return nil
}

func (*fakeSeed) LoadMeta(string, seed.ContainerHandler, timings.Measurer) error {
func (fs *fakeSeed) LoadMeta(string, seed.ContainerHandler, timings.Measurer) error {
fs.loadedSnaps = fs.essentialSnaps
return nil
}

Expand All @@ -1289,7 +1316,7 @@ func (*fakeSeed) UsesSnapdSnap() bool {
func (*fakeSeed) SetParallelism(n int) {}

func (fs *fakeSeed) EssentialSnaps() []*seed.Snap {
return fs.essentialSnaps
return fs.loadedSnaps
}

func (fs *fakeSeed) ModeSnaps(mode string) ([]*seed.Snap, error) {
Expand Down
Loading

0 comments on commit 84e9ddc

Please sign in to comment.