mirror of https://github.com/xSmurf/oz.git
parent
f23045350f
commit
351cc883f0
@ -1,122 +0,0 @@
|
|||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
// External
|
|
||||||
"github.com/op/go-logging"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (fs *Filesystem) Cleanup() error {
|
|
||||||
if fs.base == "" {
|
|
||||||
msg := "cannot Cleanup() filesystem, fs.base is empty"
|
|
||||||
fs.log.Warning(msg)
|
|
||||||
return errors.New(msg)
|
|
||||||
}
|
|
||||||
fs.log.Info("Cleanup() called on filesystem at root %s", fs.root)
|
|
||||||
|
|
||||||
for {
|
|
||||||
mnts, err := getMountsBelow(fs.base)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(mnts) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
atLeastOne, lastErr := mnts.unmountAll(fs.log)
|
|
||||||
if !atLeastOne {
|
|
||||||
return lastErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mnts mountEntries) unmountAll(log *logging.Logger) (bool, error) {
|
|
||||||
reterr := error(nil)
|
|
||||||
atLeastOne := false
|
|
||||||
for _, m := range mnts {
|
|
||||||
log.Debug("Unmounting mountpoint: %s", m.dir)
|
|
||||||
if _, err := os.Stat(m.dir); os.IsNotExist(err) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := syscall.Unmount(m.dir, 0); err != nil {
|
|
||||||
log.Warning("Failed to unmount mountpoint %s: %v", m.dir, err)
|
|
||||||
reterr = err
|
|
||||||
} else {
|
|
||||||
atLeastOne = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return atLeastOne, reterr
|
|
||||||
}
|
|
||||||
|
|
||||||
type mountEntry struct {
|
|
||||||
src string
|
|
||||||
dir string
|
|
||||||
fs string
|
|
||||||
options string
|
|
||||||
}
|
|
||||||
|
|
||||||
type mountEntries []*mountEntry
|
|
||||||
|
|
||||||
func (m mountEntries) Len() int { return len(m) }
|
|
||||||
func (m mountEntries) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
|
|
||||||
func (m mountEntries) Less(i, j int) bool { return m[i].depth() > m[j].depth() }
|
|
||||||
|
|
||||||
func (me mountEntry) depth() int { return strings.Count(me.dir, "/") }
|
|
||||||
|
|
||||||
func getMountsBelow(base string) (mountEntries, error) {
|
|
||||||
mnts, err := getProcMounts()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sort.Sort(mnts)
|
|
||||||
var filtered mountEntries
|
|
||||||
for _, m := range mnts {
|
|
||||||
if strings.HasPrefix(m.dir, base) {
|
|
||||||
filtered = append(filtered, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return filtered, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m mountEntries) contains(dir string) bool {
|
|
||||||
for _, mnt := range m {
|
|
||||||
if dir == mnt.dir {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProcMounts() (mountEntries, error) {
|
|
||||||
lines, err := readProcMounts()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var entries mountEntries
|
|
||||||
for _, line := range lines {
|
|
||||||
parts := strings.Fields(line)
|
|
||||||
if len(parts) >= 4 {
|
|
||||||
entries = append(entries, &mountEntry{
|
|
||||||
src: parts[0],
|
|
||||||
dir: parts[1],
|
|
||||||
fs: parts[2],
|
|
||||||
options: parts[3],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return entries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readProcMounts() ([]string, error) {
|
|
||||||
content, err := ioutil.ReadFile("/proc/mounts")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return strings.Split(string(content), "\n"), nil
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/op/go-logging"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MountFlag int
|
|
||||||
|
|
||||||
func (mf MountFlag) isSet(f MountFlag) bool {
|
|
||||||
return mf&f == f
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
MountReadOnly MountFlag = 1 << iota
|
|
||||||
MountCreateIfAbsent
|
|
||||||
)
|
|
||||||
|
|
||||||
type mountItem struct {
|
|
||||||
path string
|
|
||||||
target string
|
|
||||||
flags MountFlag
|
|
||||||
fs *Filesystem
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) targetPath() string {
|
|
||||||
root := mi.fs.root
|
|
||||||
if mi.target != "" {
|
|
||||||
return path.Join(root, mi.target)
|
|
||||||
}
|
|
||||||
return path.Join(root, mi.path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) bind() error {
|
|
||||||
if strings.Contains(mi.path, "*") {
|
|
||||||
return mi.bindGlobbed()
|
|
||||||
}
|
|
||||||
return mi.bindItem()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) bindGlobbed() error {
|
|
||||||
if mi.target != "" {
|
|
||||||
mi.fs.log.Warning("Ignoring target directory (%s) for mount item containing glob character: (%s)", mi.target, mi.path)
|
|
||||||
mi.target = ""
|
|
||||||
}
|
|
||||||
globbed, err := filepath.Glob(mi.path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
savedPath := mi.path
|
|
||||||
for _, p := range globbed {
|
|
||||||
if strings.Contains(p, "*") {
|
|
||||||
// XXX
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mi.path = p
|
|
||||||
if err := mi.bind(); err != nil {
|
|
||||||
// XXX
|
|
||||||
mi.path = savedPath
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mi.path = savedPath
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) readSourceInfo(src string) (os.FileInfo, error) {
|
|
||||||
if fi, err := os.Stat(src); err == nil {
|
|
||||||
return fi, nil
|
|
||||||
} else if !os.IsNotExist(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mi.flags.isSet(MountCreateIfAbsent) {
|
|
||||||
return nil, fmt.Errorf("source path (%s) does not exist", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
home := mi.fs.user.HomeDir
|
|
||||||
if !strings.HasPrefix(src, home) {
|
|
||||||
return nil, fmt.Errorf("mount item (%s) has flag MountCreateIfAbsent, but is not child of home directory (%s)", src, home)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(src, 0750); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pinfo, err := os.Stat(path.Dir(src))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := copyFileInfo(pinfo, src); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.Stat(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) bindItem() error {
|
|
||||||
src, err := filepath.EvalSymlinks(mi.path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error resolving symlinks for path (%s): %v", mi.path, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sinfo, err := mi.readSourceInfo(src)
|
|
||||||
if err != nil {
|
|
||||||
// XXX
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
target := mi.targetPath()
|
|
||||||
_, err = os.Stat(target)
|
|
||||||
if err == nil || !os.IsNotExist(err) {
|
|
||||||
mi.fs.log.Warning("Target (%s > %s) already exists, ignoring", src, target)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if sinfo.IsDir() {
|
|
||||||
if err := os.MkdirAll(target, sinfo.Mode().Perm()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := createEmptyFile(target, 0750); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := copyPathPermissions(mi.fs.root, src); err != nil {
|
|
||||||
return fmt.Errorf("failed to copy path permissions for (%s): %v", src, err)
|
|
||||||
}
|
|
||||||
return bindMount(src, target, mi.flags.isSet(MountReadOnly), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) blacklist() error {
|
|
||||||
if strings.Contains(mi.path, "*") {
|
|
||||||
return mi.blacklistGlobbed()
|
|
||||||
}
|
|
||||||
return blacklistItem(mi.path, mi.fs.log)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mi *mountItem) blacklistGlobbed() error {
|
|
||||||
globbed, err := filepath.Glob(mi.path)
|
|
||||||
if err != nil {
|
|
||||||
// XXX
|
|
||||||
}
|
|
||||||
for _, p := range globbed {
|
|
||||||
if err := blacklistItem(p, mi.fs.log); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func blacklistItem(path string, log *logging.Logger) error {
|
|
||||||
p, err := filepath.EvalSymlinks(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Warning("Symlink evaluation failed for path: %s", path)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fi, err := os.Stat(p)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
log.Info("Blacklist item (%s) does not exist", p)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
src := emptyFilePath
|
|
||||||
if fi.IsDir() {
|
|
||||||
src = emptyDirPath
|
|
||||||
}
|
|
||||||
if err := syscall.Mount(src, p, "none", syscall.MS_BIND, "mode=400,gid=0"); err != nil {
|
|
||||||
// XXX warning
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// XXX log success
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// OzInit is run from the oz-init process and performs post chroot filesystem initialization
|
|
||||||
func (fs *Filesystem) OzInit() error {
|
|
||||||
if err := fs.ozinitMountDev(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := fs.ozinitMountSysProc(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := fs.ozinitCreateSymlinks(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := fs.ozinitBlacklistItems(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) ozinitMountDev() error {
|
|
||||||
if fs.fullDevices {
|
|
||||||
flags := uintptr(syscall.MS_NOSUID | syscall.MS_REC | syscall.MS_NOEXEC)
|
|
||||||
if err := syscall.Mount("none", "/dev", "devtmpfs", flags, ""); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount devtmpfs: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mountSpecial("/dev/shm", "tmpfs", true); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount shm directory: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mountSpecial("/tmp", "tmpfs", true); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount shm directory: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mountSpecial("/dev/pts", "devpts", false); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount pts directory: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mountSpecial(path, mtype string, nodevs bool) error {
|
|
||||||
flags := uintptr(syscall.MS_NOSUID | syscall.MS_REC | syscall.MS_NOEXEC)
|
|
||||||
if nodevs {
|
|
||||||
flags = flags | syscall.MS_NODEV
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(path, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return syscall.Mount(path, path, mtype, flags, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) ozinitMountSysProc() error {
|
|
||||||
if fs.noSysAndProc {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
flags := uintptr(syscall.MS_NOSUID | syscall.MS_REC | syscall.MS_NOEXEC | syscall.MS_NODEV)
|
|
||||||
proc := "/proc"
|
|
||||||
if err := syscall.Mount("proc", proc, "proc", flags, ""); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount /proc: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
roMounts := []string{
|
|
||||||
"sysrq-trigger",
|
|
||||||
"bus",
|
|
||||||
"irq",
|
|
||||||
"sys/kernel/hotplug",
|
|
||||||
}
|
|
||||||
for _, rom := range roMounts {
|
|
||||||
p := path.Join(proc, rom)
|
|
||||||
if err := bindMount(p, p, true, 0); err != nil {
|
|
||||||
fs.log.Warning("Failed to RO mount %s: %v", p, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := syscall.Mount("sysfs", "/sys", "sysfs", syscall.MS_RDONLY|flags, ""); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount /sys: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) ozinitCreateSymlinks() error {
|
|
||||||
for _, sl := range basicSymlinks {
|
|
||||||
if err := syscall.Symlink(sl[0], sl[1]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fs.fullDevices == false {
|
|
||||||
for _, sl := range deviceSymlinks {
|
|
||||||
if err := syscall.Symlink(sl[0], sl[1]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) ozinitBlacklistItems() error {
|
|
||||||
if err := createBlacklistDir(emptyDirPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := createBlacklistFile(emptyFilePath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, item := range fs.blacklist {
|
|
||||||
if err := item.blacklist(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createBlacklistDir(path string) error {
|
|
||||||
if err := os.MkdirAll(path, 0000); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return setBlacklistPerms(path, 0500)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createBlacklistFile(path string) error {
|
|
||||||
fd, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := fd.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return setBlacklistPerms(path, 0400)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setBlacklistPerms(path string, mode os.FileMode) error {
|
|
||||||
if err := os.Chown(path, 0, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.Chmod(path, mode); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
package fs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var basicBindDirs = []string{
|
|
||||||
"/bin", "/lib", "/lib64", "/usr", "/etc",
|
|
||||||
}
|
|
||||||
|
|
||||||
var basicEmptyDirs = []string{
|
|
||||||
"/sbin", "/var", "/var/lib",
|
|
||||||
"/var/cache", "/home", "/boot",
|
|
||||||
"/tmp", "/run", "/run/user",
|
|
||||||
"/run/lock", "/root",
|
|
||||||
"/opt", "/srv", "/dev", "/proc",
|
|
||||||
"/sys", "/mnt", "/media",
|
|
||||||
//"/run/shm",
|
|
||||||
}
|
|
||||||
|
|
||||||
var basicBlacklist = []string{
|
|
||||||
"/usr/sbin", "/sbin", "${PATH}/su",
|
|
||||||
"${PATH}/sudo", "${PATH}/fusermount",
|
|
||||||
"${PATH}/xinput", "${PATH}/strace",
|
|
||||||
"${PATH}/mount", "${PATH}/umount",
|
|
||||||
}
|
|
||||||
|
|
||||||
const emptyFilePath = "/tmp/oz.ro.file"
|
|
||||||
const emptyDirPath = "/tmp/oz.ro.dir"
|
|
||||||
|
|
||||||
var basicSymlinks = [][2]string{
|
|
||||||
{"/run", "/var/run"},
|
|
||||||
{"/tmp", "/var/tmp"},
|
|
||||||
{"/run/lock", "/var/lock"},
|
|
||||||
{"/dev/shm", "/run/shm"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var deviceSymlinks = [][2]string{
|
|
||||||
{"/proc/self/fd", "/dev/fd"},
|
|
||||||
{"/proc/self/fd/2", "/dev/stderr"},
|
|
||||||
{"/proc/self/fd/0", "/dev/stdin"},
|
|
||||||
{"/proc/self/fd/1", "/dev/stdout"},
|
|
||||||
{"/dev/pts/ptmx", "/dev/ptmx"},
|
|
||||||
}
|
|
||||||
|
|
||||||
type fsDeviceDefinition struct {
|
|
||||||
path string
|
|
||||||
mode uint32
|
|
||||||
dev int
|
|
||||||
perm uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const ugorw = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP | syscall.S_IWGRP | syscall.S_IROTH | syscall.S_IWOTH
|
|
||||||
const urwgr = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP
|
|
||||||
const urw = syscall.S_IRUSR | syscall.S_IWUSR
|
|
||||||
|
|
||||||
var basicDevices = []fsDeviceDefinition{
|
|
||||||
{path: "/dev/full", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 7), perm: 0666},
|
|
||||||
{path: "/dev/null", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 3), perm: 0666},
|
|
||||||
{path: "/dev/random", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 8), perm: 0666},
|
|
||||||
|
|
||||||
{path: "/dev/console", mode: syscall.S_IFCHR | urw, dev: _makedev(5, 1), perm: 0600},
|
|
||||||
{path: "/dev/tty", mode: syscall.S_IFCHR | ugorw, dev: _makedev(5, 0), perm: 0666},
|
|
||||||
{path: "/dev/tty1", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
|
||||||
{path: "/dev/tty2", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
|
||||||
{path: "/dev/tty3", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
|
||||||
{path: "/dev/tty4", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
|
||||||
|
|
||||||
{path: "/dev/urandom", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 9), perm: 0666},
|
|
||||||
{path: "/dev/zero", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 5), perm: 0666},
|
|
||||||
}
|
|
||||||
|
|
||||||
func _makedev(x, y int) int {
|
|
||||||
return (((x) << 8) | (y))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) Setup(profilesPath string) error {
|
|
||||||
profilePathInBindDirs := false
|
|
||||||
for _, bd := range basicBindDirs {
|
|
||||||
if bd == profilesPath {
|
|
||||||
profilePathInBindDirs = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if profilePathInBindDirs == false {
|
|
||||||
basicBindDirs = append(basicBindDirs, profilesPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fs.xpra != "" {
|
|
||||||
if err := fs.createXpraDir(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
item, err := fs.newItem(fs.xpra, fs.xpra, false)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fs.whitelist = append(fs.whitelist, item)
|
|
||||||
}
|
|
||||||
if err := fs.setupRootfs(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := fs.setupChroot(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fs.fullDevices == false {
|
|
||||||
if err := fs.setupDev(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs.setupMountItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) createXpraDir() error {
|
|
||||||
uid, gid, err := userIds(fs.user)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dir := path.Join(fs.user.HomeDir, ".Xoz", fs.name)
|
|
||||||
if err := createSubdirs(fs.user.HomeDir, uid, gid, 0755, ".Xoz", fs.name); err != nil {
|
|
||||||
return fmt.Errorf("failed to create xpra directory (%s): %v", dir, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func userIds(user *user.User) (int, int, error) {
|
|
||||||
uid, err := strconv.Atoi(user.Uid)
|
|
||||||
if err != nil {
|
|
||||||
return -1, -1, errors.New("failed to parse uid from user struct: " + err.Error())
|
|
||||||
}
|
|
||||||
gid, err := strconv.Atoi(user.Gid)
|
|
||||||
if err != nil {
|
|
||||||
return -1, -1, errors.New("failed to parse gid from user struct: " + err.Error())
|
|
||||||
}
|
|
||||||
return uid, gid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) setupRootfs() error {
|
|
||||||
if err := os.MkdirAll(fs.base, 0755); err != nil {
|
|
||||||
return fmt.Errorf("unable to create directory (%s): %v", fs.base, err)
|
|
||||||
}
|
|
||||||
flags := uintptr(syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV)
|
|
||||||
data := "mode=755,gid=0"
|
|
||||||
if err := syscall.Mount(fs.base, fs.base, "tmpfs", flags, data); err != nil {
|
|
||||||
return fmt.Errorf("failed to create base tmpfs at %s: %v", fs.base, err)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
// Currently unused
|
|
||||||
// create extra directories
|
|
||||||
extra := []string{"sockets", "dev"}
|
|
||||||
for _, sub := range extra {
|
|
||||||
d := path.Join(fs.base, sub)
|
|
||||||
if err := os.Mkdir(d, 0755); err != nil {
|
|
||||||
return fmt.Errorf("unable to create directory (%s): %v", d, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) setupChroot() error {
|
|
||||||
var err error
|
|
||||||
if fs.noDefaults {
|
|
||||||
err = createEmptyDirectories(fs.root, basicBindDirs)
|
|
||||||
} else {
|
|
||||||
err = bindBasicDirectories(fs.root, basicBindDirs)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = createEmptyDirectories(fs.root, basicEmptyDirs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) setupDev() error {
|
|
||||||
devPath := path.Join(fs.root, "dev")
|
|
||||||
flags := uintptr(syscall.MS_NOSUID | syscall.MS_NOEXEC)
|
|
||||||
if err := syscall.Mount("none", devPath, "tmpfs", flags, ""); err != nil {
|
|
||||||
fs.log.Warning("Failed to mount new tmpfs: %s (%v)", devPath, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.Chmod(devPath, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dev := range basicDevices {
|
|
||||||
path := path.Join(fs.root, dev.path)
|
|
||||||
if err := syscall.Mknod(path, dev.mode, dev.dev); err != nil {
|
|
||||||
return fmt.Errorf("Failed to mknod device %s: %+v", path, err)
|
|
||||||
}
|
|
||||||
if err := os.Chmod(path, os.FileMode(dev.perm)); err != nil {
|
|
||||||
return fmt.Errorf("Unable to set permissions for device %s: %+v", dev.path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func bindBasicDirectories(root string, dirs []string) error {
|
|
||||||
for _, src := range dirs {
|
|
||||||
st, err := os.Lstat(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode := st.Mode()
|
|
||||||
target := path.Join(root, src)
|
|
||||||
if err := os.MkdirAll(target, mode.Perm()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := bindMount(src, target, true, 0); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEmptyDirectories(root string, dirs []string) error {
|
|
||||||
for _, p := range dirs {
|
|
||||||
target := path.Join(root, p)
|
|
||||||
if err := createEmptyDirectory(p, target); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEmptyDirectory(source, target string) error {
|
|
||||||
fi, err := os.Stat(source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mode := fi.Mode()
|
|
||||||
if err := os.MkdirAll(target, mode.Perm()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := copyFileInfo(fi, target); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTmp(root string) error {
|
|
||||||
target := path.Join(root, "tmp")
|
|
||||||
if err := os.Chmod(target, 0777); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return bindMount(target, target, false, syscall.MS_NOEXEC)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fs *Filesystem) setupMountItems() error {
|
|
||||||
for _, item := range fs.whitelist {
|
|
||||||
if err := item.bind(); err != nil {
|
|
||||||
// XXX
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -0,0 +1,149 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/subgraph/oz/fs"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var basicBindDirs = []string{
|
||||||
|
"/bin", "/lib", "/lib64", "/usr", "/etc",
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicEmptyDirs = []string{
|
||||||
|
"/sbin", "/var", "/var/lib",
|
||||||
|
"/var/cache", "/home", "/boot",
|
||||||
|
"/tmp", "/run", "/run/user",
|
||||||
|
"/run/lock", "/root",
|
||||||
|
"/opt", "/srv", "/dev", "/proc",
|
||||||
|
"/sys", "/mnt", "/media",
|
||||||
|
//"/run/shm",
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicSymlinks = [][2]string{
|
||||||
|
{"/run", "/var/run"},
|
||||||
|
{"/tmp", "/var/tmp"},
|
||||||
|
{"/run/lock", "/var/lock"},
|
||||||
|
{"/dev/shm", "/run/shm"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var deviceSymlinks = [][2]string{
|
||||||
|
{"/proc/self/fd", "/dev/fd"},
|
||||||
|
{"/proc/self/fd/2", "/dev/stderr"},
|
||||||
|
{"/proc/self/fd/0", "/dev/stdin"},
|
||||||
|
{"/proc/self/fd/1", "/dev/stdout"},
|
||||||
|
{"/dev/pts/ptmx", "/dev/ptmx"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicBlacklist = []string{
|
||||||
|
"/usr/sbin", "/sbin", "${PATH}/su",
|
||||||
|
"${PATH}/sudo", "${PATH}/fusermount",
|
||||||
|
"${PATH}/xinput", "${PATH}/strace",
|
||||||
|
"${PATH}/mount", "${PATH}/umount",
|
||||||
|
}
|
||||||
|
|
||||||
|
type fsDeviceDefinition struct {
|
||||||
|
path string
|
||||||
|
mode uint32
|
||||||
|
dev int
|
||||||
|
perm uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
const ugorw = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP | syscall.S_IWGRP | syscall.S_IROTH | syscall.S_IWOTH
|
||||||
|
const urwgr = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP
|
||||||
|
const urw = syscall.S_IRUSR | syscall.S_IWUSR
|
||||||
|
|
||||||
|
var basicDevices = []fsDeviceDefinition{
|
||||||
|
{path: "/dev/full", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 7), perm: 0666},
|
||||||
|
{path: "/dev/null", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 3), perm: 0666},
|
||||||
|
{path: "/dev/random", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 8), perm: 0666},
|
||||||
|
|
||||||
|
{path: "/dev/console", mode: syscall.S_IFCHR | urw, dev: _makedev(5, 1), perm: 0600},
|
||||||
|
{path: "/dev/tty", mode: syscall.S_IFCHR | ugorw, dev: _makedev(5, 0), perm: 0666},
|
||||||
|
{path: "/dev/tty1", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
||||||
|
{path: "/dev/tty2", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
||||||
|
{path: "/dev/tty3", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
||||||
|
{path: "/dev/tty4", mode: syscall.S_IFREG | urwgr, dev: 0, perm: 0640},
|
||||||
|
|
||||||
|
{path: "/dev/urandom", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 9), perm: 0666},
|
||||||
|
{path: "/dev/zero", mode: syscall.S_IFCHR | ugorw, dev: _makedev(1, 5), perm: 0666},
|
||||||
|
}
|
||||||
|
|
||||||
|
func _makedev(x, y int) int {
|
||||||
|
return (((x) << 8) | (y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRootfs(fsys *fs.Filesystem) error {
|
||||||
|
if err := os.MkdirAll(fsys.Root(), 0755); err != nil {
|
||||||
|
return fmt.Errorf("could not create rootfs path '%s': %v", fsys.Root(), err)
|
||||||
|
}
|
||||||
|
// XXX It's possible this doesn't work.
|
||||||
|
// see: https://github.com/golang/go/issues/1954
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil {
|
||||||
|
return fmt.Errorf("could not unshare mount ns: %v", err)
|
||||||
|
}
|
||||||
|
if err := syscall.Mount("", "/", "", syscall.MS_PRIVATE|syscall.MS_REC, ""); err != nil {
|
||||||
|
return fmt.Errorf("failed to set MS_PRIVATE on '%s': %v", "/", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := uintptr(syscall.MS_NOSUID | syscall.MS_NOEXEC | syscall.MS_NODEV)
|
||||||
|
if err := syscall.Mount("", fsys.Root(), "tmpfs", flags, "mode=755,gid=0"); err != nil {
|
||||||
|
return fmt.Errorf("failed to mount tmpfs on '%s': %v", fsys.Root(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := syscall.Mount("", fsys.Root(), "", syscall.MS_PRIVATE, ""); err != nil {
|
||||||
|
return fmt.Errorf("failed to set MS_PRIVATE on '%s': %v", fsys.Root(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range basicBindDirs {
|
||||||
|
if err := fsys.BindPath(p, p, true); err != nil {
|
||||||
|
return fmt.Errorf("failed to bind directory '%s': %v", p, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range basicEmptyDirs {
|
||||||
|
if err := fsys.CreateEmptyDir(p); err != nil {
|
||||||
|
return fmt.Errorf("failed to create empty directory '%s': %v", p, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dp := path.Join(fsys.Root(), "dev")
|
||||||
|
if err := syscall.Mount("", dp, "tmpfs", syscall.MS_NOSUID|syscall.MS_NOEXEC, "mode=755"); err != nil {
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
for _, d := range basicDevices {
|
||||||
|
if err := fsys.CreateDevice(d.path, d.dev, d.mode, d.perm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sl := range append(basicSymlinks, deviceSymlinks...) {
|
||||||
|
if err := fsys.CreateSymlink(sl[0], sl[1]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fsys.CreateBlacklistPaths(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bl := range basicBlacklist {
|
||||||
|
ps, err := fs.ResolvePath(bl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, p := range ps {
|
||||||
|
if err := fsys.BlacklistPath(p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in new issue