From 38f4d14017ebc6e8197e991d1b87658659612a88 Mon Sep 17 00:00:00 2001 From: David Cassany Viladomat Date: Mon, 29 Jul 2024 11:01:26 +0200 Subject: [PATCH] [backport] Disable boot entry if efivars is read-only (#2059) (#2145) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Disable boot entry if efivars is read-only (#2059) On some systems the efivars is mounted read-only if the firmware does not support writing (such as RPI). This commit turns off writing boot entries to efivars if the efivars filesystem is mounted read-only. Signed-off-by: Fredrik Lönnegren (cherry picked from commit 8baaef2251428aee5ec8c9d53f6368f417980a43) * Add test for auto disabled boot entry in Grub Signed-off-by: David Cassany (cherry picked from commit 76caad5f6ee53e33f93037ef4e2ae27bcb5e6301) --------- Co-authored-by: Fredrik Lönnegren --- pkg/action/install.go | 5 ++++- pkg/action/reset.go | 4 +++- pkg/bootloader/grub.go | 23 +++++++++++++++++++++++ pkg/bootloader/grub_test.go | 11 +++++++++-- pkg/constants/constants.go | 1 + pkg/elemental/elemental.go | 6 +++--- pkg/snapshotter/loopdevice.go | 3 ++- 7 files changed, 45 insertions(+), 8 deletions(-) diff --git a/pkg/action/install.go b/pkg/action/install.go index 877df1285a1..c0b09b211c4 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -61,7 +61,10 @@ func NewInstallAction(cfg *types.RunConfig, spec *types.InstallSpec, opts ...Ins } if i.bootloader == nil { - i.bootloader = bootloader.NewGrub(&cfg.Config, bootloader.WithGrubDisableBootEntry(i.spec.DisableBootEntry)) + i.bootloader = bootloader.NewGrub(&cfg.Config, + bootloader.WithGrubDisableBootEntry(i.spec.DisableBootEntry), + bootloader.WithGrubAutoDisableBootEntry(), + ) } if i.snapshotter == nil { diff --git a/pkg/action/reset.go b/pkg/action/reset.go index 890fa2515e4..aa652eccd77 100644 --- a/pkg/action/reset.go +++ b/pkg/action/reset.go @@ -84,7 +84,9 @@ func NewResetAction(cfg *types.RunConfig, spec *types.ResetSpec, opts ...ResetAc if r.bootloader == nil { r.bootloader = bootloader.NewGrub( - &cfg.Config, bootloader.WithGrubDisableBootEntry(r.spec.DisableBootEntry), + &cfg.Config, + bootloader.WithGrubDisableBootEntry(r.spec.DisableBootEntry), + bootloader.WithGrubAutoDisableBootEntry(), bootloader.WithGrubClearBootEntry(false), ) } diff --git a/pkg/bootloader/grub.go b/pkg/bootloader/grub.go index ba0a0a6eaa2..ec6b8dcb51e 100644 --- a/pkg/bootloader/grub.go +++ b/pkg/bootloader/grub.go @@ -23,6 +23,7 @@ import ( "regexp" "github.com/rancher/elemental-toolkit/v2/pkg/constants" + "github.com/rancher/elemental-toolkit/v2/pkg/elemental" "github.com/rancher/elemental-toolkit/v2/pkg/types" "github.com/rancher/elemental-toolkit/v2/pkg/utils" @@ -121,6 +122,28 @@ func WithGrubDisableBootEntry(disableBootEntry bool) func(g *Grub) error { } } +func WithGrubAutoDisableBootEntry() func(g *Grub) error { + return func(g *Grub) error { + if g.disableBootEntry { + // already disabled manually, doing nothing + return nil + } + + rw, err := elemental.IsRWMountPoint(g.runner, constants.EfivarsMountPath) + if err != nil { + g.logger.Errorf("error finding efivar mounts: %s", err.Error()) + return err + } + + // If efivars is not RW, disable writing boot entries. + if !rw { + g.disableBootEntry = true + } + + return nil + } +} + func WithGrubClearBootEntry(clearBootEntry bool) func(g *Grub) error { return func(g *Grub) error { g.clearBootEntry = clearBootEntry diff --git a/pkg/bootloader/grub_test.go b/pkg/bootloader/grub_test.go index fe2ff6954dc..19c51df97b8 100644 --- a/pkg/bootloader/grub_test.go +++ b/pkg/bootloader/grub_test.go @@ -105,8 +105,15 @@ var _ = Describe("Booloader", Label("bootloader", "grub"), func() { ) }) - It("installs without errors", func() { - grub = bootloader.NewGrub(cfg, bootloader.WithGrubDisableBootEntry(true)) + It("installs without errors and auto detects there is no writable efivars", func() { + runner.SideEffect = func(cmd string, args ...string) ([]byte, error) { + if cmd == "findmnt" && len(args) > 0 && args[0] == "-fno" { + return []byte("ro"), nil + } + return []byte{}, nil + } + + grub = bootloader.NewGrub(cfg, bootloader.WithGrubAutoDisableBootEntry()) Expect(grub.Install(rootDir, efiDir)).To(Succeed()) // Check grub config in EFI directory diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index d0f563fca8f..cc9ce31a2d3 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -63,6 +63,7 @@ const ( Tmpfs = "tmpfs" Autofs = "auto" Block = "block" + EfivarsMountPath = "/sys/firmware/efi/efivars" // Maxium number of nested symlinks to resolve MaxLinkDepth = 4 diff --git a/pkg/elemental/elemental.go b/pkg/elemental/elemental.go index a65cbf677c0..8a46865678d 100644 --- a/pkg/elemental/elemental.go +++ b/pkg/elemental/elemental.go @@ -177,8 +177,8 @@ func IsMounted(c types.Config, part *types.Partition) (bool, error) { return !notMnt, nil } -func IsRWMountPoint(c types.Config, mountPoint string) (bool, error) { - cmdOut, err := c.Runner.Run("findmnt", "-fno", "OPTIONS", mountPoint) +func IsRWMountPoint(r types.Runner, mountPoint string) (bool, error) { + cmdOut, err := r.Run("findmnt", "-fno", "OPTIONS", mountPoint) if err != nil { return false, err } @@ -193,7 +193,7 @@ func IsRWMountPoint(c types.Config, mountPoint string) (bool, error) { // MountRWPartition mounts, or remounts if needed, a partition with RW permissions func MountRWPartition(c types.Config, part *types.Partition) (umount func() error, err error) { if mnt, _ := IsMounted(c, part); mnt { - if ok, _ := IsRWMountPoint(c, part.MountPoint); ok { + if ok, _ := IsRWMountPoint(c.Runner, part.MountPoint); ok { c.Logger.Debugf("Already RW mounted: %s at %s", part.Name, part.MountPoint) return func() error { return nil }, nil } diff --git a/pkg/snapshotter/loopdevice.go b/pkg/snapshotter/loopdevice.go index e8ea44b8dcd..d642558d45a 100644 --- a/pkg/snapshotter/loopdevice.go +++ b/pkg/snapshotter/loopdevice.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/hashicorp/go-multierror" + "github.com/rancher/elemental-toolkit/v2/pkg/constants" "github.com/rancher/elemental-toolkit/v2/pkg/elemental" @@ -91,7 +92,7 @@ func (l *LoopDevice) InitSnapshotter(state *types.Partition, efiDir string) erro l.legacyClean = true // Legacy deployments might not include RW mounts for state partitions - if ok, _ := elemental.IsRWMountPoint(l.cfg, l.rootDir); !ok { + if ok, _ := elemental.IsRWMountPoint(l.cfg.Runner, l.rootDir); !ok { err = l.cfg.Mounter.Mount("", l.rootDir, "auto", []string{"remount", "rw"}) if err != nil { l.cfg.Logger.Errorf("Failed remounting root as RW: %v", err)