sandbox: prepare ops early
Some setup code needs to run in host root. This change allows that to happen. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
1b9408864f
commit
b74a08dda9
@ -101,6 +101,7 @@ type (
|
||||
|
||||
Ops []Op
|
||||
Op interface {
|
||||
early(params *Params) error
|
||||
apply(params *Params) error
|
||||
prefix() string
|
||||
|
||||
|
@ -98,6 +98,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
|
||||
}
|
||||
|
||||
oldmask := syscall.Umask(0)
|
||||
if params.Hostname != "" {
|
||||
if err := syscall.Sethostname([]byte(params.Hostname)); err != nil {
|
||||
log.Fatalf("cannot set hostname: %v", err)
|
||||
@ -114,6 +115,19 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
log.Fatalf("cannot make / rslave: %v", err)
|
||||
}
|
||||
|
||||
for i, op := range *params.Ops {
|
||||
if op == nil {
|
||||
log.Fatalf("invalid op %d", i)
|
||||
}
|
||||
|
||||
if err := op.early(¶ms.Params); err != nil {
|
||||
msg.PrintBaseErr(err,
|
||||
fmt.Sprintf("cannot prepare op %d:", i))
|
||||
msg.BeforeExit()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := syscall.Mount("rootfs", basePath, "tmpfs",
|
||||
syscall.MS_NODEV|syscall.MS_NOSUID,
|
||||
""); err != nil {
|
||||
@ -143,10 +157,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
}
|
||||
|
||||
for i, op := range *params.Ops {
|
||||
if op == nil {
|
||||
log.Fatalf("invalid op %d", i)
|
||||
}
|
||||
|
||||
// ops already checked during early setup
|
||||
msg.Verbosef("%s %s", op.prefix(), op)
|
||||
if err := op.apply(¶ms.Params); err != nil {
|
||||
msg.PrintBaseErr(err,
|
||||
@ -220,6 +231,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
|
||||
for i := range extraFiles {
|
||||
extraFiles[i] = os.NewFile(uintptr(offsetSetup+i), "extra file "+strconv.Itoa(i))
|
||||
}
|
||||
syscall.Umask(oldmask)
|
||||
|
||||
/*
|
||||
prepare initial process
|
||||
|
@ -14,6 +14,7 @@ const (
|
||||
BindWritable
|
||||
BindDevice
|
||||
|
||||
bindResolved
|
||||
bindAbsolute
|
||||
bindRecursive
|
||||
)
|
||||
@ -22,26 +23,21 @@ func bindMount(src, dest string, flags int) error {
|
||||
target := toSysroot(dest)
|
||||
var source string
|
||||
|
||||
if flags&BindSource == 0 {
|
||||
// this is what bwrap does, so the behaviour is kept for now,
|
||||
// however recursively resolving links might improve user experience
|
||||
if rp, err := realpathHost(src); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if flags&BindOptional != 0 {
|
||||
return nil
|
||||
} else {
|
||||
return msg.WrapErr(err,
|
||||
fmt.Sprintf("path %q does not exist", src))
|
||||
}
|
||||
}
|
||||
return msg.WrapErr(err, err.Error())
|
||||
} else {
|
||||
source = toHost(rp)
|
||||
if flags&BindSource != 0 {
|
||||
if flags&BindOptional != 0 {
|
||||
return msg.WrapErr(syscall.EINVAL,
|
||||
"flag source excludes optional")
|
||||
}
|
||||
} else if flags&bindResolved == 0 {
|
||||
return msg.WrapErr(syscall.EBADE,
|
||||
"flag source must be set on direct bind call")
|
||||
}
|
||||
|
||||
if flags&bindAbsolute != 0 {
|
||||
if flags&BindSource == 0 {
|
||||
return msg.WrapErr(syscall.EINVAL,
|
||||
"flag absolute implies source")
|
||||
}
|
||||
} else if flags&BindOptional != 0 {
|
||||
return msg.WrapErr(syscall.EINVAL,
|
||||
"flag source excludes optional")
|
||||
} else if flags&bindAbsolute != 0 {
|
||||
source = src
|
||||
} else {
|
||||
source = toHost(src)
|
||||
@ -51,8 +47,7 @@ func bindMount(src, dest string, flags int) error {
|
||||
return msg.WrapErr(err, err.Error())
|
||||
} else if fi.IsDir() {
|
||||
if err = os.MkdirAll(target, 0755); err != nil {
|
||||
return wrapErrSuffix(err,
|
||||
fmt.Sprintf("cannot create directory %q:", dest))
|
||||
return msg.WrapErr(err, err.Error())
|
||||
}
|
||||
} else if err = ensureFile(target, 0444); err != nil {
|
||||
if errors.Is(err, syscall.EISDIR) {
|
||||
@ -87,7 +82,7 @@ func bindMount(src, dest string, flags int) error {
|
||||
func mountTmpfs(fsname, name string, size int, perm os.FileMode) error {
|
||||
target := toSysroot(name)
|
||||
if err := os.MkdirAll(target, perm); err != nil {
|
||||
return err
|
||||
return msg.WrapErr(err, err.Error())
|
||||
}
|
||||
opt := fmt.Sprintf("mode=%#o", perm)
|
||||
if size > 0 {
|
||||
|
@ -26,25 +26,6 @@ func toHost(name string) string {
|
||||
return path.Join(hostPath, name)
|
||||
}
|
||||
|
||||
func realpathHost(name string) (string, error) {
|
||||
source := toHost(name)
|
||||
rp, err := os.Readlink(source)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, syscall.EINVAL) {
|
||||
// not a symlink
|
||||
return name, nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !path.IsAbs(rp) {
|
||||
return name, nil
|
||||
}
|
||||
msg.Verbosef("path %q resolves to %q", name, rp)
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
func createFile(name string, perm os.FileMode, content []byte) error {
|
||||
if err := os.MkdirAll(path.Dir(name), 0755); err != nil {
|
||||
return err
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
@ -15,17 +16,48 @@ func init() { gob.Register(new(BindMount)) }
|
||||
|
||||
// BindMount bind mounts host path Source on container path Target.
|
||||
type BindMount struct {
|
||||
Source, Target string
|
||||
Source, SourceFinal, Target string
|
||||
|
||||
Flags int
|
||||
}
|
||||
|
||||
func (b *BindMount) early(*Params) error {
|
||||
if !path.IsAbs(b.Source) {
|
||||
return msg.WrapErr(syscall.EBADE,
|
||||
fmt.Sprintf("path %q is not absolute", b.Source))
|
||||
}
|
||||
|
||||
if b.Flags&BindSource != 0 {
|
||||
b.SourceFinal = b.Source
|
||||
return nil
|
||||
}
|
||||
if v, err := filepath.EvalSymlinks(b.Source); err != nil {
|
||||
if os.IsNotExist(err) && b.Flags&BindOptional != 0 {
|
||||
b.SourceFinal = "\x00"
|
||||
return nil
|
||||
}
|
||||
return msg.WrapErr(err, err.Error())
|
||||
} else {
|
||||
b.SourceFinal = v
|
||||
b.Flags |= bindResolved
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BindMount) apply(*Params) error {
|
||||
if !path.IsAbs(b.Source) || !path.IsAbs(b.Target) {
|
||||
if b.SourceFinal == "\x00" {
|
||||
if b.Flags&BindOptional == 0 {
|
||||
// unreachable
|
||||
return syscall.EBADE
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !path.IsAbs(b.SourceFinal) || !path.IsAbs(b.Target) {
|
||||
return msg.WrapErr(syscall.EBADE,
|
||||
"path is not absolute")
|
||||
}
|
||||
return bindMount(b.Source, b.Target, b.Flags)
|
||||
return bindMount(b.SourceFinal, b.Target, b.Flags)
|
||||
}
|
||||
|
||||
func (b *BindMount) Is(op Op) bool { vb, ok := op.(*BindMount); return ok && *b == *vb }
|
||||
@ -37,7 +69,7 @@ func (b *BindMount) String() string {
|
||||
return fmt.Sprintf("%q on %q flags %#x", b.Source, b.Target, b.Flags&BindWritable)
|
||||
}
|
||||
func (f *Ops) Bind(source, target string, flags int) *Ops {
|
||||
*f = append(*f, &BindMount{source, target, flags | bindRecursive})
|
||||
*f = append(*f, &BindMount{source, "", target, flags | bindRecursive})
|
||||
return f
|
||||
}
|
||||
|
||||
@ -46,6 +78,7 @@ func init() { gob.Register(new(MountProc)) }
|
||||
// MountProc mounts a private instance of proc.
|
||||
type MountProc string
|
||||
|
||||
func (p MountProc) early(*Params) error { return nil }
|
||||
func (p MountProc) apply(*Params) error {
|
||||
v := string(p)
|
||||
|
||||
@ -76,6 +109,7 @@ func init() { gob.Register(new(MountDev)) }
|
||||
// MountDev mounts part of host dev.
|
||||
type MountDev string
|
||||
|
||||
func (d MountDev) early(*Params) error { return nil }
|
||||
func (d MountDev) apply(params *Params) error {
|
||||
v := string(d)
|
||||
|
||||
@ -135,7 +169,7 @@ func (d MountDev) apply(params *Params) error {
|
||||
syscall.SYS_IOCTL, 1, syscall.TIOCGWINSZ,
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
); errno == 0 {
|
||||
if err := bindMount("/proc/self/fd/1", path.Join(v, "console"), BindDevice); err != nil {
|
||||
if err := bindMount("/proc/self/fd/1", path.Join(v, "console"), BindSource|BindDevice); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -157,6 +191,7 @@ func init() { gob.Register(new(MountMqueue)) }
|
||||
// MountMqueue mounts a private mqueue instance on container Path.
|
||||
type MountMqueue string
|
||||
|
||||
func (m MountMqueue) early(*Params) error { return nil }
|
||||
func (m MountMqueue) apply(*Params) error {
|
||||
v := string(m)
|
||||
|
||||
@ -191,6 +226,7 @@ type MountTmpfs struct {
|
||||
Perm os.FileMode
|
||||
}
|
||||
|
||||
func (t *MountTmpfs) early(*Params) error { return nil }
|
||||
func (t *MountTmpfs) apply(*Params) error {
|
||||
if !path.IsAbs(t.Path) {
|
||||
return msg.WrapErr(syscall.EBADE,
|
||||
@ -216,6 +252,7 @@ func init() { gob.Register(new(Symlink)) }
|
||||
// Symlink creates a symlink in the container filesystem.
|
||||
type Symlink [2]string
|
||||
|
||||
func (l *Symlink) early(*Params) error { return nil }
|
||||
func (l *Symlink) apply(*Params) error {
|
||||
// symlink target is an arbitrary path value, so only validate link name here
|
||||
if !path.IsAbs(l[1]) {
|
||||
@ -241,6 +278,7 @@ func init() { gob.Register(new(Mkdir)) }
|
||||
// Mkdir creates a directory in the container filesystem.
|
||||
type Mkdir string
|
||||
|
||||
func (m Mkdir) early(*Params) error { return nil }
|
||||
func (m Mkdir) apply(*Params) error {
|
||||
v := string(m)
|
||||
|
||||
@ -272,6 +310,7 @@ type Tmpfile struct {
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (t *Tmpfile) early(*Params) error { return nil }
|
||||
func (t *Tmpfile) apply(*Params) error {
|
||||
if !path.IsAbs(t.Path) {
|
||||
return msg.WrapErr(syscall.EBADE,
|
||||
|
Loading…
Reference in New Issue
Block a user