Skip to content

Commit

Permalink
Copy grub+shim from the rootfs especific paths (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
Itxaka authored Jan 11, 2024
1 parent 845b9b1 commit ae41cbf
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 48 deletions.
19 changes: 2 additions & 17 deletions pkg/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
package constants

import (
"fmt"
"os"
)

Expand Down Expand Up @@ -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"
Expand All @@ -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:
Expand Down
122 changes: 93 additions & 29 deletions pkg/utils/grub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
16 changes: 14 additions & 2 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down

0 comments on commit ae41cbf

Please sign in to comment.