mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-08-02 08:38:47 +00:00
Copy grub+shim from the rootfs especific paths (#189)
This commit is contained in:
parent
845b9b1e3b
commit
ae41cbf34e
@ -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:
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user