diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-07-03 02:59:43 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-07-03 02:59:43 +0900 |
| commit | 1b5ecd9eaf3289d164d8ed1bce6013e0e4ef8e86 (patch) | |
| tree | 047fe5fabdfb37d5c0088f7f572cebc8b13853bb /container.go | |
| parent | 82561d62b66f17c05604e87f18187bb3a91f00d2 (diff) | |
container: move out of toplevel
This allows slightly easier use of the vanity url. This also provides some disambiguation between low level containers and hakurei app containers.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container.go')
| -rw-r--r-- | container.go | 229 |
1 files changed, 0 insertions, 229 deletions
diff --git a/container.go b/container.go deleted file mode 100644 index 5bbc5d3c..00000000 --- a/container.go +++ /dev/null @@ -1,229 +0,0 @@ -// Package hakurei implements unprivileged Linux containers with built-in support for syscall filtering. -package hakurei - -import ( - "context" - "encoding/gob" - "errors" - "fmt" - "io" - "os" - "os/exec" - "path" - "strconv" - . "syscall" - "time" - - "git.gensokyo.uk/security/hakurei/seccomp" -) - -type ( - // Container represents a container environment being prepared or run. - // None of [Container] methods are safe for concurrent use. - Container struct { - // Name of initial process in the container. - name string - // Cgroup fd, nil to disable. - Cgroup *int - // ExtraFiles passed through to initial process in the container, - // with behaviour identical to its [exec.Cmd] counterpart. - ExtraFiles []*os.File - - // Custom [exec.Cmd] initialisation function. - CommandContext func(ctx context.Context) (cmd *exec.Cmd) - - // param encoder for shim and init - setup *gob.Encoder - // cancels cmd - cancel context.CancelFunc - - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer - - Cancel func(cmd *exec.Cmd) error - WaitDelay time.Duration - - cmd *exec.Cmd - ctx context.Context - Params - } - - // Params holds container configuration and is safe to serialise. - Params struct { - // Working directory in the container. - Dir string - // Initial process environment. - Env []string - // Absolute path of initial process in the container. Overrides name. - Path string - // Initial process argv. - Args []string - - // Mapped Uid in user namespace. - Uid int - // Mapped Gid in user namespace. - Gid int - // Hostname value in UTS namespace. - Hostname string - // Sequential container setup ops. - *Ops - // Seccomp system call filter rules. - SeccompRules []seccomp.NativeRule - // Extra seccomp flags. - SeccompFlags seccomp.ExportFlag - // Seccomp presets. Has no effect unless SeccompRules is zero-length. - SeccompPresets seccomp.FilterPreset - // Do not load seccomp program. - SeccompDisable bool - // Permission bits of newly created parent directories. - // The zero value is interpreted as 0755. - ParentPerm os.FileMode - // Do not syscall.Setsid. - RetainSession bool - // Do not [syscall.CLONE_NEWNET]. - HostNet bool - // Retain CAP_SYS_ADMIN. - Privileged bool - } -) - -func (p *Container) Start() error { - if p.cmd != nil { - return errors.New("sandbox: already started") - } - if p.Ops == nil || len(*p.Ops) == 0 { - return errors.New("sandbox: starting an empty container") - } - - ctx, cancel := context.WithCancel(p.ctx) - p.cancel = cancel - - var cloneFlags uintptr = CLONE_NEWIPC | CLONE_NEWUTS | CLONE_NEWCGROUP - if !p.HostNet { - cloneFlags |= CLONE_NEWNET - } - - // map to overflow id to work around ownership checks - if p.Uid < 1 { - p.Uid = OverflowUid() - } - if p.Gid < 1 { - p.Gid = OverflowGid() - } - - if !p.RetainSession { - p.SeccompPresets |= seccomp.PresetDenyTTY - } - - if p.CommandContext != nil { - p.cmd = p.CommandContext(ctx) - } else { - p.cmd = exec.CommandContext(ctx, MustExecutable()) - p.cmd.Args = []string{"init"} - } - - p.cmd.Stdin, p.cmd.Stdout, p.cmd.Stderr = p.Stdin, p.Stdout, p.Stderr - p.cmd.WaitDelay = p.WaitDelay - if p.Cancel != nil { - p.cmd.Cancel = func() error { return p.Cancel(p.cmd) } - } else { - p.cmd.Cancel = func() error { return p.cmd.Process.Signal(SIGTERM) } - } - p.cmd.Dir = "/" - p.cmd.SysProcAttr = &SysProcAttr{ - Setsid: !p.RetainSession, - Pdeathsig: SIGKILL, - Cloneflags: cloneFlags | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS, - - // remain privileged for setup - AmbientCaps: []uintptr{CAP_SYS_ADMIN, CAP_SETPCAP}, - - UseCgroupFD: p.Cgroup != nil, - } - if p.cmd.SysProcAttr.UseCgroupFD { - p.cmd.SysProcAttr.CgroupFD = *p.Cgroup - } - - // place setup pipe before user supplied extra files, this is later restored by init - if fd, e, err := Setup(&p.cmd.ExtraFiles); err != nil { - return wrapErrSuffix(err, - "cannot create shim setup pipe:") - } else { - p.setup = e - p.cmd.Env = []string{setupEnv + "=" + strconv.Itoa(fd)} - } - p.cmd.ExtraFiles = append(p.cmd.ExtraFiles, p.ExtraFiles...) - - msg.Verbose("starting container init") - if err := p.cmd.Start(); err != nil { - return msg.WrapErr(err, err.Error()) - } - return nil -} - -func (p *Container) Serve() error { - if p.setup == nil { - panic("invalid serve") - } - - setup := p.setup - p.setup = nil - - if p.Path != "" && !path.IsAbs(p.Path) { - p.cancel() - return msg.WrapErr(EINVAL, - fmt.Sprintf("invalid executable path %q", p.Path)) - } - - if p.Path == "" { - if p.name == "" { - p.Path = os.Getenv("SHELL") - if !path.IsAbs(p.Path) { - p.cancel() - return msg.WrapErr(EBADE, - "no command specified and $SHELL is invalid") - } - p.name = path.Base(p.Path) - } else if path.IsAbs(p.name) { - p.Path = p.name - } else if v, err := exec.LookPath(p.name); err != nil { - p.cancel() - return msg.WrapErr(err, err.Error()) - } else { - p.Path = v - } - } - - if p.SeccompRules == nil { - // do not transmit nil - p.SeccompRules = make([]seccomp.NativeRule, 0) - } - - err := setup.Encode( - &initParams{ - p.Params, - Getuid(), - Getgid(), - len(p.ExtraFiles), - msg.IsVerbose(), - }, - ) - if err != nil { - p.cancel() - } - return err -} - -func (p *Container) Wait() error { defer p.cancel(); return p.cmd.Wait() } - -func (p *Container) String() string { - return fmt.Sprintf("argv: %q, filter: %v, rules: %d, flags: %#x, presets: %#x", - p.Args, !p.SeccompDisable, len(p.SeccompRules), int(p.SeccompFlags), int(p.SeccompPresets)) -} - -func New(ctx context.Context, name string, args ...string) *Container { - return &Container{name: name, ctx: ctx, - Params: Params{Args: append([]string{name}, args...), Dir: "/", Ops: new(Ops)}, - } -} |
