mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
Refactor container mounts
This commit is contained in:
parent
085f7a63c2
commit
c832edba58
@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -171,9 +172,10 @@ func start() (*runner.Result, error) {
|
|||||||
// work dir
|
// work dir
|
||||||
WithTmpfs("w", "size=8m,nr_inodes=4k").
|
WithTmpfs("w", "size=8m,nr_inodes=4k").
|
||||||
// tmp dir
|
// tmp dir
|
||||||
WithTmpfs("tmp", "size=8m,nr_inodes=4k")
|
WithTmpfs("tmp", "size=8m,nr_inodes=4k").
|
||||||
|
FilterNotExist()
|
||||||
|
|
||||||
mt, err := mb.Build(true)
|
mt, err := mb.FilterNotExist().Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -264,10 +266,15 @@ func start() (*runner.Result, error) {
|
|||||||
if cred {
|
if cred {
|
||||||
credG = newCredGen()
|
credG = newCredGen()
|
||||||
}
|
}
|
||||||
|
var stderr io.Writer
|
||||||
|
if showDetails {
|
||||||
|
stderr = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
b := container.Builder{
|
b := container.Builder{
|
||||||
Root: root,
|
Root: root,
|
||||||
Mounts: mt,
|
Mounts: mb.Mounts,
|
||||||
|
Stderr: stderr,
|
||||||
CredGenerator: credG,
|
CredGenerator: credG,
|
||||||
CloneFlags: forkexec.UnshareFlags,
|
CloneFlags: forkexec.UnshareFlags,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,8 @@ func BenchmarkContainer(b *testing.B) {
|
|||||||
b.Error(err)
|
b.Error(err)
|
||||||
}
|
}
|
||||||
builder := &Builder{
|
builder := &Builder{
|
||||||
Root: tmpDir,
|
Root: tmpDir,
|
||||||
|
Stderr: os.Stderr,
|
||||||
}
|
}
|
||||||
n := runtime.GOMAXPROCS(0)
|
n := runtime.GOMAXPROCS(0)
|
||||||
ch := make(chan Environment, n)
|
ch := make(chan Environment, n)
|
||||||
@ -61,7 +62,7 @@ func TestContainerSuccess(t *testing.T) {
|
|||||||
})
|
})
|
||||||
r := <-rt
|
r := <-rt
|
||||||
if r.Status != runner.StatusNormal {
|
if r.Status != runner.StatusNormal {
|
||||||
t.Error(r.Status, r.Error)
|
t.Fatal(r.Status, r.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ func TestContainerSetCred(t *testing.T) {
|
|||||||
})
|
})
|
||||||
r := <-rt
|
r := <-rt
|
||||||
if r.Status != runner.StatusNormal {
|
if r.Status != runner.StatusNormal {
|
||||||
t.Error(r.Status, r.Error)
|
t.Fatal(r.Status, r.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ func TestContainerNotExists(t *testing.T) {
|
|||||||
})
|
})
|
||||||
r := <-rt
|
r := <-rt
|
||||||
if r.Status != runner.StatusRunnerError {
|
if r.Status != runner.StatusRunnerError {
|
||||||
t.Error(r.Status, r.Error)
|
t.Fatal(r.Status, r.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,14 +117,14 @@ func TestContainerSyncFuncFail(t *testing.T) {
|
|||||||
})
|
})
|
||||||
r := <-rt
|
r := <-rt
|
||||||
if r.Status != runner.StatusRunnerError {
|
if r.Status != runner.StatusRunnerError {
|
||||||
t.Error(r.Status, r.Error)
|
t.Fatal(r.Status, r.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnv(t *testing.T, credGen CredGenerator) Environment {
|
func getEnv(t *testing.T, credGen CredGenerator) Environment {
|
||||||
tmpDir, err := ioutil.TempDir("", "")
|
tmpDir, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
os.Remove(tmpDir)
|
os.Remove(tmpDir)
|
||||||
@ -131,10 +132,11 @@ func getEnv(t *testing.T, credGen CredGenerator) Environment {
|
|||||||
builder := &Builder{
|
builder := &Builder{
|
||||||
Root: tmpDir,
|
Root: tmpDir,
|
||||||
CredGenerator: credGen,
|
CredGenerator: credGen,
|
||||||
|
Stderr: os.Stderr,
|
||||||
}
|
}
|
||||||
m, err := builder.Build()
|
m, err := builder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
m.Destroy()
|
m.Destroy()
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const (
|
|||||||
cmdKill = "kill"
|
cmdKill = "kill"
|
||||||
cmdConf = "conf"
|
cmdConf = "conf"
|
||||||
|
|
||||||
initArg = "init"
|
initArg = "container_init"
|
||||||
|
|
||||||
currentExec = "/proc/self/exe"
|
currentExec = "/proc/self/exe"
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,9 @@ func (c *containerServer) handlePing() error {
|
|||||||
func (c *containerServer) handleConf(conf *confCmd) error {
|
func (c *containerServer) handleConf(conf *confCmd) error {
|
||||||
if conf != nil {
|
if conf != nil {
|
||||||
c.containerConfig = conf.Conf
|
c.containerConfig = conf.Conf
|
||||||
|
if err := initContainer(conf.Conf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return c.sendReply(&reply{}, nil)
|
return c.sendReply(&reply{}, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/criyle/go-sandbox/pkg/unixsocket"
|
"github.com/criyle/go-sandbox/pkg/unixsocket"
|
||||||
)
|
)
|
||||||
@ -20,7 +21,7 @@ type containerServer struct {
|
|||||||
func Init() (err error) {
|
func Init() (err error) {
|
||||||
// noop if self is not container init process
|
// noop if self is not container init process
|
||||||
// Notice: docker init is also 1, additional check for args[1] == init
|
// Notice: docker init is also 1, additional check for args[1] == init
|
||||||
if os.Getpid() != 1 || len(os.Args) != 2 || os.Args[1] != initArg {
|
if os.Getpid() != 1 || len(os.Args) < 2 || os.Args[1] != initArg {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,3 +92,54 @@ func (c *containerServer) handleCmd(cmd *cmd, msg *unixsocket.Msg) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("unknown command: %s", cmd.Cmd)
|
return fmt.Errorf("unknown command: %s", cmd.Cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initContainer(c containerConfig) error {
|
||||||
|
if err := initFileSystem(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.Setdomainname([]byte(c.DomainName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := syscall.Sethostname([]byte(c.HostName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Chdir(c.WorkDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFileSystem(c containerConfig) error {
|
||||||
|
// mount tmpfs as root
|
||||||
|
const tmpfs = "tmpfs"
|
||||||
|
if err := syscall.Mount(tmpfs, c.ContainerRoot, tmpfs, 0, ""); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: mount / %v", err)
|
||||||
|
}
|
||||||
|
// change dir to container root
|
||||||
|
if err := syscall.Chdir(c.ContainerRoot); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: chdir %v", err)
|
||||||
|
}
|
||||||
|
// performing mounts
|
||||||
|
for _, m := range c.Mounts {
|
||||||
|
if err := m.Mount(); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: mount %v %v", m, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// pivot root
|
||||||
|
const oldRoot = "old_root"
|
||||||
|
if err := os.Mkdir(oldRoot, 0755); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: mkdir(old_root) %v", err)
|
||||||
|
}
|
||||||
|
if err := syscall.PivotRoot(c.ContainerRoot, oldRoot); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: pivot_root(%s, %s) %v", c.ContainerRoot, oldRoot, err)
|
||||||
|
}
|
||||||
|
if err := syscall.Unmount(oldRoot, syscall.MNT_DETACH); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: unmount(old_root) %v", err)
|
||||||
|
}
|
||||||
|
if err := os.Remove(oldRoot); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: unlink(old_root) %v", err)
|
||||||
|
}
|
||||||
|
// readonly root
|
||||||
|
const remountFlag = syscall.MS_BIND | syscall.MS_REMOUNT | syscall.MS_RDONLY | syscall.MS_NOATIME | syscall.MS_NOSUID
|
||||||
|
if err := syscall.Mount(tmpfs, "/", tmpfs, remountFlag, ""); err != nil {
|
||||||
|
return fmt.Errorf("init_fs: readonly remount / %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -3,7 +3,9 @@ package container
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@ -23,10 +25,13 @@ type Builder struct {
|
|||||||
Root string
|
Root string
|
||||||
|
|
||||||
// Mounts defines container mount points, empty uses default mounts
|
// Mounts defines container mount points, empty uses default mounts
|
||||||
Mounts []mount.SyscallParams
|
Mounts []mount.Mount
|
||||||
|
|
||||||
|
// WorkDir defines container default work directory (default: /w)
|
||||||
|
WorkDir string
|
||||||
|
|
||||||
// Stderr defines whether to dup container stderr to stderr for debug
|
// Stderr defines whether to dup container stderr to stderr for debug
|
||||||
Stderr bool
|
Stderr io.Writer
|
||||||
|
|
||||||
// ExecFile defines executable that called Init, otherwise defer current
|
// ExecFile defines executable that called Init, otherwise defer current
|
||||||
// executable (/proc/self/exe)
|
// executable (/proc/self/exe)
|
||||||
@ -37,6 +42,12 @@ type Builder struct {
|
|||||||
|
|
||||||
// Clone flags defines unshare clone flag to create container
|
// Clone flags defines unshare clone flag to create container
|
||||||
CloneFlags uintptr
|
CloneFlags uintptr
|
||||||
|
|
||||||
|
// HostName set container hostname (default: go-sandbox)
|
||||||
|
HostName string
|
||||||
|
|
||||||
|
// DomainName set container domainname (default: go-sandbox)
|
||||||
|
DomainName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// CredGenerator generates uid / gid credential used by container
|
// CredGenerator generates uid / gid credential used by container
|
||||||
@ -57,28 +68,31 @@ type Environment interface {
|
|||||||
|
|
||||||
// container manages single pre-forked container environment
|
// container manages single pre-forked container environment
|
||||||
type container struct {
|
type container struct {
|
||||||
pid int // underlying container init pid
|
process *os.Process // underlying container init pid
|
||||||
socket *socket // host - container communication
|
socket *socket // host - container communication
|
||||||
mu sync.Mutex // lock to avoid race condition
|
mu sync.Mutex // lock to avoid race condition
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build creates new environment with underlying container
|
// Build creates new environment with underlying container
|
||||||
func (b *Builder) Build() (Environment, error) {
|
func (b *Builder) Build() (Environment, error) {
|
||||||
var (
|
c, err := b.startContainer()
|
||||||
err error
|
if err != nil {
|
||||||
cred syscall.Credential
|
return nil, err
|
||||||
uidMap, gidMap []syscall.SysProcIDMap
|
}
|
||||||
)
|
|
||||||
|
// avoid non cinit enabled executable running as container init process
|
||||||
|
if err = c.Ping(); err != nil {
|
||||||
|
c.Destroy()
|
||||||
|
return nil, fmt.Errorf("container: container init not responding to ping %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// container mount points
|
// container mount points
|
||||||
mounts := b.Mounts
|
mounts := b.Mounts
|
||||||
if len(mounts) == 0 {
|
if len(mounts) == 0 {
|
||||||
if mounts, err = mount.NewDefaultBuilder().
|
mounts = mount.NewDefaultBuilder().
|
||||||
WithTmpfs("w", ""). // work dir
|
WithTmpfs("w", ""). // work dir
|
||||||
WithTmpfs("tmp", ""). // tmp
|
WithTmpfs("tmp", ""). // tmp
|
||||||
Build(true); err != nil {
|
FilterNotExist().Mounts
|
||||||
return nil, fmt.Errorf("container: failed to build rootfs mount %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// container root directory on the host
|
// container root directory on the host
|
||||||
@ -88,29 +102,40 @@ func (b *Builder) Build() (Environment, error) {
|
|||||||
return nil, fmt.Errorf("container: failed to get work directory %v", err)
|
return nil, fmt.Errorf("container: failed to get work directory %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
workDir := containerWD
|
||||||
// prepare stdin / stdout / stderr
|
if b.WorkDir != "" {
|
||||||
devNull, err := os.OpenFile(os.DevNull, os.O_RDWR, os.ModePerm)
|
workDir = b.WorkDir
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("container: failed to open devNull %v", err)
|
|
||||||
}
|
}
|
||||||
defer devNull.Close()
|
hostName := containerName
|
||||||
|
if b.HostName != "" {
|
||||||
files := make([]uintptr, 0, 4)
|
hostName = b.HostName
|
||||||
files = append(files, devNull.Fd(), devNull.Fd())
|
}
|
||||||
if b.Stderr {
|
domainName := containerName
|
||||||
files = append(files, os.Stderr.Fd())
|
if b.DomainName != "" {
|
||||||
} else {
|
domainName = b.DomainName
|
||||||
files = append(files, devNull.Fd())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare container exec file
|
// set configuration and check if container creation successful
|
||||||
execFile, err := b.exec()
|
if err = c.conf(&containerConfig{
|
||||||
if err != nil {
|
WorkDir: workDir,
|
||||||
return nil, fmt.Errorf("container: prepare exec %v", err)
|
HostName: hostName,
|
||||||
|
DomainName: domainName,
|
||||||
|
ContainerRoot: root,
|
||||||
|
Mounts: mounts,
|
||||||
|
Cred: b.CredGenerator != nil,
|
||||||
|
}); err != nil {
|
||||||
|
c.Destroy()
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
defer execFile.Close()
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) startContainer() (*container, error) {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
cred syscall.Credential
|
||||||
|
uidMap, gidMap []syscall.SysProcIDMap
|
||||||
|
)
|
||||||
// prepare host <-> container unix socket
|
// prepare host <-> container unix socket
|
||||||
ins, outs, err := newPassCredSocketPair()
|
ins, outs, err := newPassCredSocketPair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,12 +150,13 @@ func (b *Builder) Build() (Environment, error) {
|
|||||||
}
|
}
|
||||||
defer outf.Close()
|
defer outf.Close()
|
||||||
|
|
||||||
files = append(files, uintptr(outf.Fd()))
|
|
||||||
|
|
||||||
// prepare container running credential
|
// prepare container running credential
|
||||||
if b.CredGenerator != nil {
|
if b.CredGenerator != nil {
|
||||||
cred = b.CredGenerator.Get()
|
cred = b.CredGenerator.Get()
|
||||||
uidMap, gidMap = getIDMapping(&cred)
|
uidMap, gidMap = getIDMapping(&cred)
|
||||||
|
} else {
|
||||||
|
uidMap = []syscall.SysProcIDMap{{HostID: os.Geteuid(), Size: 1}}
|
||||||
|
gidMap = []syscall.SysProcIDMap{{HostID: os.Getegid(), Size: 1}}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cloneFlag uintptr
|
var cloneFlag uintptr
|
||||||
@ -140,45 +166,34 @@ func (b *Builder) Build() (Environment, error) {
|
|||||||
cloneFlag = b.CloneFlags & forkexec.UnshareFlags
|
cloneFlag = b.CloneFlags & forkexec.UnshareFlags
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &forkexec.Runner{
|
args := []string{os.Args[0], initArg}
|
||||||
Args: []string{os.Args[0], initArg},
|
if b.ExecFile != "" {
|
||||||
Env: []string{PathEnv},
|
args[0] = b.ExecFile
|
||||||
ExecFile: execFile.Fd(),
|
|
||||||
Files: files,
|
|
||||||
WorkDir: containerWD,
|
|
||||||
CloneFlags: cloneFlag,
|
|
||||||
Mounts: mounts,
|
|
||||||
HostName: containerName,
|
|
||||||
DomainName: containerName,
|
|
||||||
PivotRoot: root,
|
|
||||||
UIDMappings: uidMap,
|
|
||||||
GIDMappings: gidMap,
|
|
||||||
}
|
}
|
||||||
pid, err := r.Start()
|
|
||||||
if err != nil {
|
r := exec.Cmd{
|
||||||
|
Path: args[0],
|
||||||
|
Args: args,
|
||||||
|
Env: []string{PathEnv},
|
||||||
|
Stderr: b.Stderr,
|
||||||
|
ExtraFiles: []*os.File{outf},
|
||||||
|
SysProcAttr: &syscall.SysProcAttr{
|
||||||
|
Cloneflags: cloneFlag,
|
||||||
|
UidMappings: uidMap,
|
||||||
|
GidMappings: gidMap,
|
||||||
|
AmbientCaps: []uintptr{
|
||||||
|
unix.CAP_SYS_ADMIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err = r.Start(); err != nil {
|
||||||
ins.Close()
|
ins.Close()
|
||||||
return nil, fmt.Errorf("container: failed to start container %v", err)
|
return nil, fmt.Errorf("container: failed to start container %v", err)
|
||||||
}
|
}
|
||||||
|
return &container{
|
||||||
c := &container{
|
process: r.Process,
|
||||||
pid: pid,
|
socket: newSocket(ins),
|
||||||
socket: newSocket(ins),
|
}, nil
|
||||||
}
|
|
||||||
// avoid non cinit enabled executable running as container init process
|
|
||||||
if err = c.Ping(); err != nil {
|
|
||||||
c.Destroy()
|
|
||||||
return nil, fmt.Errorf("container: container init not responding to ping %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set configuration and check if container creation successful
|
|
||||||
if err = c.conf(&containerConfig{
|
|
||||||
Cred: b.CredGenerator != nil,
|
|
||||||
}); err != nil {
|
|
||||||
c.Destroy()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy kill the container process (with its children)
|
// Destroy kill the container process (with its children)
|
||||||
@ -192,13 +207,8 @@ func (c *container) Destroy() error {
|
|||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
// kill process
|
// kill process
|
||||||
var wstatus unix.WaitStatus
|
c.process.Kill()
|
||||||
unix.Kill(c.pid, unix.SIGKILL)
|
_, err := c.process.Wait()
|
||||||
// wait for container process to exit
|
|
||||||
_, err := unix.Wait4(c.pid, &wstatus, 0, nil)
|
|
||||||
for err == unix.EINTR {
|
|
||||||
_, err = unix.Wait4(c.pid, &wstatus, 0, nil)
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/criyle/go-sandbox/pkg/unixsocket"
|
"github.com/criyle/go-sandbox/pkg/unixsocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ping send ping message to container
|
// Ping send ping message to container, wait for 3 second before timeout
|
||||||
func (c *container) Ping() error {
|
func (c *container) Ping() error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/criyle/go-sandbox/pkg/mount"
|
||||||
"github.com/criyle/go-sandbox/pkg/rlimit"
|
"github.com/criyle/go-sandbox/pkg/rlimit"
|
||||||
"github.com/criyle/go-sandbox/runner"
|
"github.com/criyle/go-sandbox/runner"
|
||||||
)
|
)
|
||||||
@ -47,6 +48,14 @@ type confCmd struct {
|
|||||||
|
|
||||||
// ContainerConfig set the container config
|
// ContainerConfig set the container config
|
||||||
type containerConfig struct {
|
type containerConfig struct {
|
||||||
|
WorkDir string
|
||||||
|
|
||||||
|
HostName string
|
||||||
|
DomainName string
|
||||||
|
|
||||||
|
ContainerRoot string
|
||||||
|
Mounts []mount.Mount
|
||||||
|
|
||||||
Cred bool
|
Cred bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
4
go.mod
4
go.mod
@ -6,8 +6,8 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/elastic/go-seccomp-bpf v1.1.0
|
github.com/elastic/go-seccomp-bpf v1.1.0
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
|
||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/elastic/go-seccomp-bpf => ../go-seccomp-bpf
|
replace github.com/elastic/go-seccomp-bpf => ../go-seccomp-bpf
|
||||||
|
|||||||
12
go.sum
12
go.sum
@ -16,14 +16,16 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
@ -173,7 +173,7 @@ func getMounts(dirs []string) []mount.SyscallParams {
|
|||||||
Flags: roBind,
|
Flags: roBind,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
m, _ := builder.Build(true)
|
m, _ := builder.FilterNotExist().Build()
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ func TestFork_ENOENT(t *testing.T) {
|
|||||||
WithMount(
|
WithMount(
|
||||||
mount.Mount{
|
mount.Mount{
|
||||||
Source: "NOT_EXISTS",
|
Source: "NOT_EXISTS",
|
||||||
}).Build(false)
|
}).Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,16 +22,12 @@ func NewDefaultBuilder() *Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build creates sequence of syscalls for fork_exec
|
// Build creates sequence of syscalls for fork_exec
|
||||||
// skipNotExists skips bind mounts that source not exists
|
func (b *Builder) Build() ([]SyscallParams, error) {
|
||||||
func (b *Builder) Build(skipNotExists bool) ([]SyscallParams, error) {
|
|
||||||
var err error
|
var err error
|
||||||
ret := make([]SyscallParams, 0, len(b.Mounts))
|
ret := make([]SyscallParams, 0, len(b.Mounts))
|
||||||
for _, m := range b.Mounts {
|
for _, m := range b.Mounts {
|
||||||
var mknod bool
|
var mknod bool
|
||||||
if mknod, err = isBindMountFileOrNotExists(m); err != nil {
|
if mknod, err = isBindMountFileOrNotExists(m); err != nil {
|
||||||
if skipNotExists {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sp, err := m.ToSyscall()
|
sp, err := m.ToSyscall()
|
||||||
@ -44,8 +40,23 @@ func (b *Builder) Build(skipNotExists bool) ([]SyscallParams, error) {
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterNotExist removes bind mount that does not exists
|
||||||
|
func (b *Builder) FilterNotExist() *Builder {
|
||||||
|
rt := b.Mounts[:0]
|
||||||
|
for _, m := range b.Mounts {
|
||||||
|
if m.IsBindMount() {
|
||||||
|
if _, err := os.Stat(m.Source); os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rt = append(rt, m)
|
||||||
|
}
|
||||||
|
b.Mounts = rt
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func isBindMountFileOrNotExists(m Mount) (bool, error) {
|
func isBindMountFileOrNotExists(m Mount) (bool, error) {
|
||||||
if m.Flags&unix.MS_BIND == unix.MS_BIND {
|
if m.IsBindMount() {
|
||||||
if fi, err := os.Stat(m.Source); os.IsNotExist(err) {
|
if fi, err := os.Stat(m.Source); os.IsNotExist(err) {
|
||||||
return false, err
|
return false, err
|
||||||
} else if !fi.IsDir() {
|
} else if !fi.IsDir() {
|
||||||
|
|||||||
@ -3,12 +3,13 @@ package mount
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mount calls mount syscall
|
// Mount calls mount syscall
|
||||||
func (m *Mount) Mount() error {
|
func (m *Mount) Mount() error {
|
||||||
if err := os.MkdirAll(m.Target, 0755); err != nil {
|
if err := ensureMountTargetExists(m.Source, m.Target); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := syscall.Mount(m.Source, m.Target, m.FsType, m.Flags, m.Data); err != nil {
|
if err := syscall.Mount(m.Source, m.Target, m.FsType, m.Flags, m.Data); err != nil {
|
||||||
@ -24,6 +25,36 @@ func (m *Mount) Mount() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBindMount returns if it is a bind mount
|
||||||
|
func (m Mount) IsBindMount() bool {
|
||||||
|
return m.Flags&syscall.MS_BIND == syscall.MS_BIND
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsReadOnly returns if it is a readonly mount
|
||||||
|
func (m Mount) IsReadOnly() bool {
|
||||||
|
return m.Flags&syscall.MS_RDONLY == syscall.MS_RDONLY
|
||||||
|
}
|
||||||
|
|
||||||
|
func ensureMountTargetExists(source, target string) error {
|
||||||
|
isFile := false
|
||||||
|
if fi, err := os.Stat(source); err == nil {
|
||||||
|
isFile = !fi.IsDir()
|
||||||
|
}
|
||||||
|
dir := target
|
||||||
|
if isFile {
|
||||||
|
dir = filepath.Dir(target)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isFile {
|
||||||
|
if err := syscall.Mknod(target, 0755, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m Mount) String() string {
|
func (m Mount) String() string {
|
||||||
switch {
|
switch {
|
||||||
case m.Flags&syscall.MS_BIND == syscall.MS_BIND:
|
case m.Flags&syscall.MS_BIND == syscall.MS_BIND:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user