From ae41cbf34e5d148710610b8410f577d8b74f0aa2 Mon Sep 17 00:00:00 2001 From: Itxaka Date: Thu, 11 Jan 2024 11:24:43 +0100 Subject: [PATCH] Copy grub+shim from the rootfs especific paths (#189) --- pkg/constants/constants.go | 19 +----- pkg/utils/grub.go | 122 ++++++++++++++++++++++++++++--------- pkg/utils/utils_test.go | 16 ++++- 3 files changed, 109 insertions(+), 48 deletions(-) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 663ba96c..46cac348 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -17,7 +17,6 @@ limitations under the License. package constants import ( - "fmt" "os" ) @@ -105,8 +104,8 @@ const ( Archx86 = "x86_64" ArchArm64 = "arm64" SignedShim = "shim.efi" - - Rsync = "rsync" + SignedGrub = "grub.efi" + Rsync = "rsync" UkiSource = "/run/install/uki" UkiCdromSource = "/run/install/cdrom" @@ -128,20 +127,6 @@ func GetDefaultSquashfsCompressionOptions() []string { return []string{"-comp", "gzip"} } -func GetGrubFilePaths(arch string) []string { - var archPath string - switch arch { - case ArchArm64: - archPath = "aarch64" - default: - archPath = Archx86 - } - return []string{ - fmt.Sprintf("/usr/share/efi/%s/grub.efi", archPath), - fmt.Sprintf("/usr/share/efi/%s/%s", archPath, SignedShim), - } -} - func GetFallBackEfi(arch string) string { switch arch { case ArchArm64: diff --git a/pkg/utils/grub.go b/pkg/utils/grub.go index 08c45c2d..ae165997 100644 --- a/pkg/utils/grub.go +++ b/pkg/utils/grub.go @@ -20,6 +20,7 @@ import ( "fmt" agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config" "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs" + "github.com/kairos-io/kairos-sdk/utils" "io/fs" "path/filepath" "strings" @@ -169,39 +170,22 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool, } // Copy needed files for efi boot - shimFiles := cnst.GetGrubFilePaths(g.config.Arch) - - for _, f := range shimFiles { - _, name := filepath.Split(f) - fileWriteName := filepath.Join(cnst.EfiDir, fmt.Sprintf("EFI/boot/%s", name)) - g.config.Logger.Debugf("Copying %s to %s", f, fileWriteName) - - // They are bundled in the rootfs so pick them from there - fileContent, err := g.config.Fs.ReadFile(filepath.Join(cnst.ActiveDir, f)) - - if err != nil { - return fmt.Errorf("error reading %s: %s", filepath.Join(cnst.ActiveDir, f), err) - } - err = g.config.Fs.WriteFile(fileWriteName, fileContent, cnst.FilePerm) - if err != nil { - return fmt.Errorf("error writing %s: %s", fileWriteName, err) - } - } - - // Rename the shimName to the fallback name so the system boots from fallback. This means that we do not create - // any bootloader entries, so our recent installation has the lower priority if something else is on the bootloader - writeShim := cnst.GetFallBackEfi(g.config.Arch) - - readShim, err := g.config.Fs.ReadFile(filepath.Join(cnst.EfiDir, "EFI/boot/", cnst.SignedShim)) + // This seems like a chore while we could provide a package for those bundled files as they are just a shim and a grub efi + // BUT this is needed for secureboot + // The shim contains the signature from microsoft and the shim provider (i.e. upstream distro like fedora, suse, etc) + // So if we use the shim+grub from a generic package (i.e. ubuntu) it WILL boot with secureboot + // but when loading the kernel it will fail because the kernel is not signed by the shim provider or grub provider + // the kernel signature would be from fedora while the shim signature would be from ubuntu + // This is why if we want to support secureboot we need to copy the shim+grub from the rootfs default paths instead of + // providing a generic package + err = g.copyShim() if err != nil { - return fmt.Errorf("could not read shim file %s at dir %s", cnst.SignedShim, cnst.EfiDir) + return err } - - err = g.config.Fs.WriteFile(filepath.Join(cnst.EfiDir, "EFI/boot/", writeShim), readShim, cnst.FilePerm) + err = g.copyGrub() if err != nil { - return fmt.Errorf("could not write shim file %s at dir %s", writeShim, cnst.EfiDir) + return err } - // Add grub.cfg in EFI that chainloads the grub.cfg in recovery // Notice that we set the config to /grub2/grub.cfg which means the above we need to copy the file from // the installation source into that dir @@ -260,3 +244,83 @@ func copyGrubFonts(cfg *agentConfig.Config, rootDir, bootDir, systemgrub string) } } } + +func (g Grub) copyShim() error { + shimFiles := utils.GetEfiShimFiles(g.config.Arch) + shimDone := false + for _, f := range shimFiles { + _, err := g.config.Fs.Stat(filepath.Join(cnst.ActiveDir, f)) + if err != nil { + g.config.Logger.Debugf("skip copying %s: not found", filepath.Join(cnst.ActiveDir, f)) + continue + } + _, name := filepath.Split(f) + // remove the .signed suffix if present + name, _ = strings.CutSuffix(name, ".signed") + fileWriteName := filepath.Join(cnst.EfiDir, fmt.Sprintf("EFI/boot/%s", name)) + g.config.Logger.Debugf("Copying %s to %s", f, fileWriteName) + + // Try to find the paths give until we succeed + fileContent, err := g.config.Fs.ReadFile(filepath.Join(cnst.ActiveDir, f)) + + if err != nil { + g.config.Logger.Warnf("error reading %s: %s", filepath.Join(cnst.ActiveDir, f), err) + continue + } + err = g.config.Fs.WriteFile(fileWriteName, fileContent, cnst.FilePerm) + if err != nil { + return fmt.Errorf("error writing %s: %s", fileWriteName, err) + } + shimDone = true + + // Copy the shim content to the fallback name so the system boots from fallback. This means that we do not create + // any bootloader entries, so our recent installation has the lower priority if something else is on the bootloader + writeShim := cnst.GetFallBackEfi(g.config.Arch) + err = g.config.Fs.WriteFile(filepath.Join(cnst.EfiDir, "EFI/boot/", writeShim), fileContent, cnst.FilePerm) + if err != nil { + return fmt.Errorf("could not write shim file %s at dir %s", writeShim, cnst.EfiDir) + } + break + } + if !shimDone { + g.config.Logger.Debugf("List of shim files searched for: %s", shimFiles) + return fmt.Errorf("could not find any shim file to copy") + } + return nil +} + +func (g Grub) copyGrub() error { + grubFiles := utils.GetEfiGrubFiles(g.config.Arch) + grubDone := false + for _, f := range grubFiles { + _, err := g.config.Fs.Stat(filepath.Join(constants.ActiveDir, f)) + if err != nil { + g.config.Logger.Debugf("skip copying %s: not found", filepath.Join(constants.ActiveDir, f)) + continue + } + _, name := filepath.Split(f) + // remove the .signed suffix if present + name, _ = strings.CutSuffix(name, ".signed") + fileWriteName := filepath.Join(cnst.EfiDir, fmt.Sprintf("EFI/boot/%s", name)) + g.config.Logger.Debugf("Copying %s to %s", f, fileWriteName) + + // Try to find the paths give until we succeed + fileContent, err := g.config.Fs.ReadFile(filepath.Join(cnst.ActiveDir, f)) + + if err != nil { + g.config.Logger.Warnf("error reading %s: %s", filepath.Join(cnst.ActiveDir, f), err) + continue + } + err = g.config.Fs.WriteFile(fileWriteName, fileContent, cnst.FilePerm) + if err != nil { + return fmt.Errorf("error writing %s: %s", fileWriteName, err) + } + grubDone = true + break + } + if !grubDone { + g.config.Logger.Debugf("List of grub files searched for: %s", grubFiles) + return fmt.Errorf("could not find any grub efi file to copy") + } + return nil +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 29b09e42..e45c2796 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -830,7 +830,7 @@ var _ = Describe("Utils", Label("utils"), func() { Expect(err.Error()).To(ContainSubstring("grub")) Expect(err.Error()).To(ContainSubstring("modules")) }) - It("fails with efi if no grub files exist", Label("efi"), func() { + It("fails with efi if no shim/grub files exist", Label("efi"), func() { err := fsutils.MkdirAll(fs, filepath.Join(rootDir, "/x86_64/"), constants.DirPerm) Expect(err).ShouldNot(HaveOccurred()) err = fs.WriteFile(filepath.Join(rootDir, "/x86_64/loopback.mod"), []byte(""), constants.FilePerm) @@ -840,7 +840,19 @@ var _ = Describe("Utils", Label("utils"), func() { grub := utils.NewGrub(config) err = grub.Install(target, rootDir, bootDir, constants.GrubConf, "", true, "") Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("no such file")) + Expect(err.Error()).To(ContainSubstring("could not find any shim file to copy")) + // Create fake shim + err = fsutils.MkdirAll(fs, filepath.Join(rootDir, "/usr/share/efi/x86_64/"), constants.DirPerm) + Expect(err).ShouldNot(HaveOccurred()) + err = fs.WriteFile(filepath.Join(rootDir, "/usr/share/efi/x86_64/", constants.SignedShim), []byte(""), constants.FilePerm) + Expect(err).ShouldNot(HaveOccurred()) + err = fs.WriteFile(filepath.Join(rootDir, "/usr/share/efi/x86_64/shim.efi"), []byte(""), constants.FilePerm) + Expect(err).ShouldNot(HaveOccurred()) + grub = utils.NewGrub(config) + err = grub.Install(target, rootDir, bootDir, constants.GrubConf, "", true, "") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("could not find any grub efi file to copy")) + }) It("installs with extra tty", func() { err := fs.Mkdir("/dev", constants.DirPerm)