mirror of
https://github.com/kairos-io/immucore.git
synced 2025-04-27 19:16:59 +00:00
Chroot into a new dir before starting the system (#234)
* Chroot into a new dir before starting the system Signed-off-by: Itxaka <itxaka@kairos.io> * Use ReadDir and copy files int eh rootdir to the enw rootdir Signed-off-by: Itxaka <itxaka@kairos.io> * logggg Signed-off-by: Itxaka <itxaka@kairos.io> * Several fixes Mount /dev at start so we can log to kmesg/ttyS0 Log more Store the mountpoints found in root to bind them later to the new sysroot Signed-off-by: Itxaka <itxaka@kairos.io> * debvuy Signed-off-by: Itxaka <itxaka@kairos.io> * Fix Signed-off-by: Itxaka <itxaka@kairos.io> * more debufg Signed-off-by: Itxaka <itxaka@kairos.io> * fix Signed-off-by: Itxaka <itxaka@kairos.io> * sfder Signed-off-by: Itxaka <itxaka@kairos.io> * Fix symlinks Signed-off-by: Itxaka <itxaka@kairos.io> * final Signed-off-by: Itxaka <itxaka@kairos.io> * disable mobving the / root mountpoint Im not sure this works on our side or how, I just get errors Signed-off-by: Itxaka <itxaka@kairos.io> * Debug Signed-off-by: Itxaka <itxaka@kairos.io> * Disable remounting / as RO and enable remounting the new sysroot as RO Signed-off-by: Itxaka <itxaka@kairos.io> * Dont drop to bash like that Signed-off-by: Itxaka <itxaka@kairos.io> * Move "sysroot" to a constant, dry code and handle errors Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Fix linting errors by removing superflows `else` statements because the `if`s end with `continue` Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> * Apply suggestions from code review * Remove loggers that don't work because `/dev` is not there yet (?). In any case, we need to switch to the new logger Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> --------- Signed-off-by: Itxaka <itxaka@kairos.io> Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me> Co-authored-by: Dimitris Karakasilis <dimitris@karakasilis.me>
This commit is contained in:
parent
a52b9651ad
commit
25975a5594
1
go.mod
1
go.mod
@ -114,6 +114,7 @@ require (
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/samber/lo v1.37.0 // indirect
|
||||
github.com/sanity-io/litter v1.5.5 // indirect
|
||||
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b // indirect
|
||||
github.com/sergi/go-diff v1.3.1 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
|
4
go.sum
4
go.sum
@ -136,6 +136,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -460,6 +461,7 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR
|
||||
github.com/pkg/xattr v0.4.1/go.mod h1:W2cGD0TBEus7MkUgv0tNZ9JutLtVO3cXu+IBRuHqnFs=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@ -500,6 +502,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||
github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw=
|
||||
github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM=
|
||||
github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
@ -539,6 +542,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
@ -103,6 +103,7 @@ const (
|
||||
UkiDefaultcdrom = "/dev/sr0"
|
||||
UkiDefaultcdromFsType = "iso9660"
|
||||
UkiDefaultEfiimgFsType = "vfat"
|
||||
UkiSysrootDir = "sysroot"
|
||||
PersistentStateTarget = "/usr/local/.state"
|
||||
LogDir = "/run/immucore"
|
||||
)
|
||||
|
@ -11,6 +11,9 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sanity-io/litter"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/foxboron/go-uefi/efi"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
cnst "github.com/kairos-io/immucore/internal/constants"
|
||||
@ -20,7 +23,6 @@ import (
|
||||
kcrypt "github.com/kairos-io/kcrypt/pkg/lib"
|
||||
"github.com/mudler/go-kdetect"
|
||||
"github.com/spectrocloud-labs/herd"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// MountTmpfsDagStep adds the step to mount /tmp .
|
||||
@ -490,49 +492,18 @@ func (s *State) UKIMountBaseSystem(g *herd.Graph) error {
|
||||
)
|
||||
}
|
||||
|
||||
// UKIBootInitDagStep tries to launch /sbin/init in root and pass over the system
|
||||
// booting to the real init process
|
||||
// Drops to emergency if not able to. Panic if it cant even launch emergency.
|
||||
func (s *State) UKIBootInitDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpUkiInit,
|
||||
herd.WeakDeps,
|
||||
herd.WithWeakDeps(cnst.OpRemountRootRO, cnst.OpRootfsHook, cnst.OpInitramfsHook, cnst.OpWriteFstab),
|
||||
herd.WithCallback(func(ctx context.Context) error {
|
||||
output, err := internalUtils.CommandWithPath("/usr/lib/systemd/systemd-pcrphase --graceful leave-initrd")
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Msg("running systemd-pcrphase")
|
||||
internalUtils.Log.Debug().Str("out", output).Msg("systemd-pcrphase leave-initrd")
|
||||
}
|
||||
// Print dag before exit, otherwise its never printed as we never exit the program
|
||||
internalUtils.Log.Info().Msg(s.WriteDAG(g))
|
||||
internalUtils.Log.Debug().Msg("Executing init callback!")
|
||||
internalUtils.CloseLogFiles()
|
||||
if err := unix.Exec("/sbin/init", []string{"/sbin/init", "--system"}, os.Environ()); err != nil {
|
||||
internalUtils.Log.Err(err).Msg("running init")
|
||||
// drop to emergency shell
|
||||
if err := unix.Exec("/bin/bash", []string{"/bin/bash"}, os.Environ()); err != nil {
|
||||
internalUtils.Log.Fatal().Msg("Could not drop to emergency shell")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
// UKIRemountRootRODagStep remount root read only.
|
||||
func (s *State) UKIRemountRootRODagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpRemountRootRO,
|
||||
herd.WithDeps(cnst.OpRootfsHook),
|
||||
herd.WithCallback(func(ctx context.Context) error {
|
||||
var err error
|
||||
for i := 1; i < 5; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
// Should we try to stop udev here?
|
||||
err = syscall.Mount("", "/", "", syscall.MS_REMOUNT|syscall.MS_RDONLY, "")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Create the /sysroot dir before remounting as RO
|
||||
err := os.MkdirAll(s.path(cnst.UkiSysrootDir), 0755)
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Str("path", s.path(cnst.UkiSysrootDir)).Msg("Creating sysroot")
|
||||
return err
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
}
|
||||
@ -808,3 +779,142 @@ func (s *State) MountLiveCd(g *herd.Graph, opts ...herd.OpOption) error {
|
||||
return nil
|
||||
}))...)
|
||||
}
|
||||
|
||||
// UKIBootInitDagStep tries to launch /sbin/init in root and pass over the system
|
||||
// booting to the real init process
|
||||
// Drops to emergency if not able to. Panic if it cant even launch emergency.
|
||||
func (s *State) UKIBootInitDagStep(g *herd.Graph) error {
|
||||
return g.Add(cnst.OpUkiInit,
|
||||
herd.WeakDeps,
|
||||
herd.WithWeakDeps(cnst.OpRemountRootRO, cnst.OpRootfsHook, cnst.OpInitramfsHook, cnst.OpWriteFstab),
|
||||
herd.WithCallback(func(ctx context.Context) error {
|
||||
output, err := internalUtils.CommandWithPath("/usr/lib/systemd/systemd-pcrphase --graceful leave-initrd")
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Msg("running systemd-pcrphase")
|
||||
internalUtils.Log.Debug().Str("out", output).Msg("systemd-pcrphase leave-initrd")
|
||||
}
|
||||
// Print dag before exit, otherwise its never printed as we never exit the program
|
||||
internalUtils.Log.Info().Msg(s.WriteDAG(g))
|
||||
|
||||
// Mount a tmpfs under sysroot
|
||||
err = syscall.Mount("tmpfs", s.path(cnst.UkiSysrootDir), "tmpfs", syscall.MS_NOSUID|syscall.MS_NODEV|syscall.MS_NOEXEC, "")
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Msg("mounting tmpfs on sysroot")
|
||||
}
|
||||
|
||||
// Move all the dirs in root FS that are not a mountpoint to the new root via Bind mount
|
||||
rootDirs, err := os.ReadDir(s.Rootdir)
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Msg("reading rootdir content")
|
||||
}
|
||||
mountPoints := []string{}
|
||||
internalUtils.Log.Debug().Str("s", litter.Sdump(rootDirs)).Msg("Moving root dirs to sysroot")
|
||||
for _, file := range rootDirs {
|
||||
if file.Name() == cnst.UkiSysrootDir {
|
||||
continue
|
||||
}
|
||||
if file.IsDir() {
|
||||
path := file.Name()
|
||||
fileInfo, err := os.Stat(s.path(path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentPath := filepath.Dir(s.path(path))
|
||||
parentInfo, err := os.Stat(parentPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If the directory has the same device as its parent, it's not a mount point.
|
||||
if fileInfo.Sys().(*syscall.Stat_t).Dev == parentInfo.Sys().(*syscall.Stat_t).Dev {
|
||||
internalUtils.Log.Debug().Msg(fmt.Sprintf("%s is a simple directory", path))
|
||||
err = os.MkdirAll(filepath.Join(s.path(cnst.UkiSysrootDir), path), 0755)
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Str("what", filepath.Join(s.path(cnst.UkiSysrootDir), path)).Msg("mkdir")
|
||||
return err
|
||||
}
|
||||
// Bind mount it
|
||||
err = syscall.Mount(s.path(path), filepath.Join(s.path(cnst.UkiSysrootDir), path), "", syscall.MS_BIND, "")
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Str("what", s.path(path)).
|
||||
Str("where", filepath.Join(s.path(cnst.UkiSysrootDir), path)).Msg("bind mount")
|
||||
return err
|
||||
}
|
||||
internalUtils.Log.Debug().Msg(fmt.Sprintf("Bind mounted %s to %s", s.path(path), filepath.Join(s.path(cnst.UkiSysrootDir), path)))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
internalUtils.Log.Debug().Msg(fmt.Sprintf("%s is a mount point, skipping", s.path(path)))
|
||||
mountPoints = append(mountPoints, s.path(path))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
info, _ := file.Info()
|
||||
fileInfo, _ := os.Lstat(file.Name())
|
||||
|
||||
// Symlink
|
||||
if fileInfo.Mode()&os.ModeSymlink != 0 {
|
||||
target, err := os.Readlink(file.Name())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read symlink: %w", err)
|
||||
}
|
||||
symlinkPath := s.path(filepath.Join(cnst.UkiSysrootDir, file.Name()))
|
||||
err = os.Symlink(target, symlinkPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create symlink: %w", err)
|
||||
}
|
||||
internalUtils.Log.Debug().Str("from", target).Str("to", symlinkPath).Msg("Symlinked file")
|
||||
} else {
|
||||
// If its a file in the root dir just copy it over
|
||||
content, _ := os.ReadFile(s.path(file.Name()))
|
||||
newFilePath := s.path(filepath.Join(cnst.UkiSysrootDir, file.Name()))
|
||||
_ = os.WriteFile(newFilePath, content, info.Mode())
|
||||
internalUtils.Log.Debug().Msg(fmt.Sprintf("Copied %s to %s", s.path(file.Name()), newFilePath))
|
||||
}
|
||||
}
|
||||
// Now move the system mounts into the new dir
|
||||
for _, d := range mountPoints {
|
||||
newDir := filepath.Join(s.path(cnst.UkiSysrootDir), d)
|
||||
err := os.MkdirAll(newDir, 0755)
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Str("what", newDir).Msg("mkdir")
|
||||
}
|
||||
err = syscall.Mount(filepath.Join(s.path(), d), newDir, "", syscall.MS_MOVE, "")
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Str("what", filepath.Join(s.Rootdir, d)).Str("where", newDir).Msg("move mount")
|
||||
continue
|
||||
}
|
||||
internalUtils.Log.Debug().Str("from", filepath.Join(s.path(), d)).Str("to", newDir).Msg("Mount moved")
|
||||
}
|
||||
|
||||
// remount sysroot as readonly before chrooting
|
||||
if err = syscall.Mount("", s.path(cnst.UkiSysrootDir), "", syscall.MS_REMOUNT|syscall.MS_RDONLY, ""); err != nil {
|
||||
internalUtils.Log.Err(err).Msg("re-mounting sysroot as read only")
|
||||
}
|
||||
|
||||
// Now chdir+chroot into the new dir
|
||||
if err := unix.Chdir(s.path(cnst.UkiSysrootDir)); err != nil {
|
||||
internalUtils.Log.Err(err).Msg("chdir")
|
||||
return fmt.Errorf("failed change directory to new_root %v", err)
|
||||
}
|
||||
internalUtils.Log.Debug().Msg("Chdir to sysroot done")
|
||||
|
||||
err = unix.Chroot(".")
|
||||
if err != nil {
|
||||
internalUtils.Log.Err(err).Msg("chroot")
|
||||
return fmt.Errorf("failed to chroot %v", err)
|
||||
}
|
||||
internalUtils.Log.Debug().Msg("Chroot to sysroot done")
|
||||
|
||||
internalUtils.Log.Debug().Msg("Executing init callback!")
|
||||
if err := unix.Exec("/sbin/init", []string{"/sbin/init"}, os.Environ()); err != nil {
|
||||
internalUtils.Log.Err(err).Msg("running init")
|
||||
// drop to emergency shell
|
||||
if err := unix.Exec("/bin/bash", []string{"/bin/bash"}, os.Environ()); err != nil {
|
||||
internalUtils.Log.Fatal().Msg("Could not drop to emergency shell")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user