sandbox: apply vfs options to bind mounts
Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
8c3a817881
commit
c638193268
@ -111,7 +111,7 @@ func TestContainer(t *testing.T) {
|
|||||||
mnt = append(mnt,
|
mnt = append(mnt,
|
||||||
&check.Mntent{FSName: "tmpfs", Dir: "/tmp", Type: "tmpfs", Opts: "host_passthrough"},
|
&check.Mntent{FSName: "tmpfs", Dir: "/tmp", Type: "tmpfs", Opts: "host_passthrough"},
|
||||||
&check.Mntent{FSName: "\x00", Dir: os.Args[0], Type: "\x00", Opts: "\x00"},
|
&check.Mntent{FSName: "\x00", Dir: os.Args[0], Type: "\x00", Opts: "\x00"},
|
||||||
&check.Mntent{FSName: "rootfs", Dir: "/etc/hostname", Type: "tmpfs", Opts: "host_passthrough"},
|
&check.Mntent{FSName: "rootfs", Dir: "/etc/hostname", Type: "tmpfs", Opts: "\x00"},
|
||||||
)
|
)
|
||||||
for _, name := range libPaths {
|
for _, name := range libPaths {
|
||||||
mnt = append(mnt, &check.Mntent{FSName: "\x00", Dir: name, Type: "\x00", Opts: "\x00", Freq: -1, Passno: -1})
|
mnt = append(mnt, &check.Mntent{FSName: "\x00", Dir: name, Type: "\x00", Opts: "\x00", Freq: -1, Passno: -1})
|
||||||
|
@ -1,22 +1,102 @@
|
|||||||
package sandbox
|
package sandbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"git.gensokyo.uk/security/fortify/sandbox/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) error {
|
func (p *procPaths) bindMount(source, target string, flags uintptr, eq bool) error {
|
||||||
var mf uintptr = syscall.MS_SILENT | syscall.MS_BIND
|
|
||||||
mf |= flags & syscall.MS_REC
|
|
||||||
if eq {
|
if eq {
|
||||||
msg.Verbosef("resolved %q flags %#x", target, mf)
|
msg.Verbosef("resolved %q flags %#x", target, flags)
|
||||||
} else {
|
} else {
|
||||||
msg.Verbosef("resolved %q on %q flags %#x", source, target, mf)
|
msg.Verbosef("resolved %q on %q flags %#x", source, target, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapErrSuffix(syscall.Mount(source, target, "", mf, ""),
|
if err := syscall.Mount(source, target, "",
|
||||||
fmt.Sprintf("cannot mount %q on %q:", source, target))
|
syscall.MS_SILENT|syscall.MS_BIND|flags&syscall.MS_REC, ""); err != nil {
|
||||||
|
return wrapErrSuffix(err,
|
||||||
|
fmt.Sprintf("cannot mount %q on %q:", source, target))
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetFinal string
|
||||||
|
if v, err := filepath.EvalSymlinks(target); err != nil {
|
||||||
|
return msg.WrapErr(err, err.Error())
|
||||||
|
} else {
|
||||||
|
targetFinal = v
|
||||||
|
if targetFinal != target {
|
||||||
|
msg.Verbosef("target resolves to %q", targetFinal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final target path according to the kernel through proc
|
||||||
|
var targetKFinal string
|
||||||
|
{
|
||||||
|
var destFd int
|
||||||
|
if err := IgnoringEINTR(func() (err error) {
|
||||||
|
destFd, err = syscall.Open(targetFinal, O_PATH|syscall.O_CLOEXEC, 0)
|
||||||
|
return
|
||||||
|
}); err != nil {
|
||||||
|
return wrapErrSuffix(err,
|
||||||
|
fmt.Sprintf("cannot open %q:", targetFinal))
|
||||||
|
}
|
||||||
|
if v, err := os.Readlink(p.fd(destFd)); err != nil {
|
||||||
|
return msg.WrapErr(err, err.Error())
|
||||||
|
} else if err = syscall.Close(destFd); err != nil {
|
||||||
|
return wrapErrSuffix(err,
|
||||||
|
fmt.Sprintf("cannot close %q:", targetFinal))
|
||||||
|
} else {
|
||||||
|
targetKFinal = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mf := syscall.MS_NOSUID | flags&syscall.MS_NODEV | flags&syscall.MS_RDONLY
|
||||||
|
return hostProc.mountinfo(func(d *vfs.MountInfoDecoder) error {
|
||||||
|
n, err := d.Unfold(targetKFinal)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, syscall.ESTALE) {
|
||||||
|
return msg.WrapErr(err,
|
||||||
|
fmt.Sprintf("mount point %q never appeared in mountinfo", targetKFinal))
|
||||||
|
}
|
||||||
|
return wrapErrSuffix(err,
|
||||||
|
"cannot unfold mount hierarchy:")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = remountWithFlags(n, mf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if flags&syscall.MS_REC == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for cur := range n.Collective() {
|
||||||
|
err = remountWithFlags(cur, mf)
|
||||||
|
if err != nil && !errors.Is(err, syscall.EACCES) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func remountWithFlags(n *vfs.MountInfoNode, mf uintptr) error {
|
||||||
|
kf, unmatched := n.Flags()
|
||||||
|
if len(unmatched) != 0 {
|
||||||
|
msg.Verbosef("unmatched vfs options: %q", unmatched)
|
||||||
|
}
|
||||||
|
|
||||||
|
if kf&mf != mf {
|
||||||
|
return wrapErrSuffix(syscall.Mount("none", n.Clean, "",
|
||||||
|
syscall.MS_SILENT|syscall.MS_BIND|syscall.MS_REMOUNT|kf|mf,
|
||||||
|
""),
|
||||||
|
fmt.Sprintf("cannot remount %q:", n.Clean))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mountTmpfs(fsname, name string, size int, perm os.FileMode) error {
|
func mountTmpfs(fsname, name string, size int, perm os.FileMode) error {
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"git.gensokyo.uk/security/fortify/sandbox/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -64,14 +66,29 @@ func ensureFile(name string, perm os.FileMode) error {
|
|||||||
var hostProc = newProcPats(hostPath)
|
var hostProc = newProcPats(hostPath)
|
||||||
|
|
||||||
func newProcPats(prefix string) *procPaths {
|
func newProcPats(prefix string) *procPaths {
|
||||||
return &procPaths{prefix, prefix + "/self", prefix + "/self/mountinfo"}
|
return &procPaths{prefix + "/proc", prefix + "/proc/self"}
|
||||||
}
|
}
|
||||||
|
|
||||||
type procPaths struct {
|
type procPaths struct {
|
||||||
prefix string
|
prefix string
|
||||||
self string
|
self string
|
||||||
mountinfo string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *procPaths) stdout() string { return p.self + "/fd/1" }
|
func (p *procPaths) stdout() string { return p.self + "/fd/1" }
|
||||||
func (p *procPaths) fd(fd int) string { return p.self + "/fd/" + strconv.Itoa(fd) }
|
func (p *procPaths) fd(fd int) string { return p.self + "/fd/" + strconv.Itoa(fd) }
|
||||||
|
func (p *procPaths) mountinfo(f func(d *vfs.MountInfoDecoder) error) error {
|
||||||
|
if r, err := os.Open(p.self + "/mountinfo"); err != nil {
|
||||||
|
return msg.WrapErr(err, err.Error())
|
||||||
|
} else {
|
||||||
|
d := vfs.NewMountInfoDecoder(r)
|
||||||
|
err0 := f(d)
|
||||||
|
if err = r.Close(); err != nil {
|
||||||
|
return wrapErrSuffix(err,
|
||||||
|
"cannot close mountinfo:")
|
||||||
|
} else if err = d.Err(); err != nil {
|
||||||
|
return wrapErrSuffix(err,
|
||||||
|
"cannot parse mountinfo:")
|
||||||
|
}
|
||||||
|
return err0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user