diff options
Diffstat (limited to 'internal/app/container.go')
| -rw-r--r-- | internal/app/container.go | 253 |
1 files changed, 0 insertions, 253 deletions
diff --git a/internal/app/container.go b/internal/app/container.go deleted file mode 100644 index eef4e226..00000000 --- a/internal/app/container.go +++ /dev/null @@ -1,253 +0,0 @@ -package app - -import ( - "errors" - "fmt" - "io/fs" - "maps" - "path" - "syscall" - - "hakurei.app/container" - "hakurei.app/container/seccomp" - "hakurei.app/hst" - "hakurei.app/system/dbus" -) - -// in practice there should be less than 30 system mount points -const preallocateOpsCount = 1 << 5 - -// newContainer initialises [container.Params] via [hst.ContainerConfig]. -// Note that remaining container setup must be queued by the caller. -func newContainer( - msg container.Msg, - k syscallDispatcher, - s *hst.ContainerConfig, - prefix string, - sc *hst.Paths, - uid, gid *int, -) (*container.Params, map[string]string, error) { - if s == nil { - return nil, nil, newWithMessage("invalid container configuration") - } - - params := &container.Params{ - Hostname: s.Hostname, - RetainSession: s.Tty, - HostNet: s.HostNet, - HostAbstract: s.HostAbstract, - - // the container is canceled when shim is requested to exit or receives an interrupt or termination signal; - // this behaviour is implemented in the shim - ForwardCancel: s.WaitDelay >= 0, - } - - as := &hst.ApplyState{AutoEtcPrefix: prefix} - { - ops := make(container.Ops, 0, preallocateOpsCount+len(s.Filesystem)) - params.Ops = &ops - as.Ops = &ops - } - - if s.Multiarch { - params.SeccompFlags |= seccomp.AllowMultiarch - } - - if !s.SeccompCompat { - params.SeccompPresets |= seccomp.PresetExt - } - if !s.Devel { - params.SeccompPresets |= seccomp.PresetDenyDevel - } - if !s.Userns { - params.SeccompPresets |= seccomp.PresetDenyNS - } - if !s.Tty { - params.SeccompPresets |= seccomp.PresetDenyTTY - } - - if s.MapRealUID { - params.Uid = k.getuid() - *uid = params.Uid - params.Gid = k.getgid() - *gid = params.Gid - } else { - *uid = k.overflowUid(msg) - *gid = k.overflowGid(msg) - } - - filesystem := s.Filesystem - var autoroot *hst.FSBind - // valid happens late, so root mount gets it here - if len(filesystem) > 0 && filesystem[0].Valid() && filesystem[0].Path().String() == container.FHSRoot { - // if the first element targets /, it is inserted early and excluded from path hiding - rootfs := filesystem[0].FilesystemConfig - filesystem = filesystem[1:] - rootfs.Apply(as) - - // autoroot requires special handling during path hiding - if b, ok := rootfs.(*hst.FSBind); ok && b.IsAutoRoot() { - autoroot = b - } - } - - params. - Proc(container.AbsFHSProc). - Tmpfs(hst.AbsTmp, 1<<12, 0755) - - if !s.Device { - params.DevWritable(container.AbsFHSDev, true) - } else { - params.Bind(container.AbsFHSDev, container.AbsFHSDev, container.BindWritable|container.BindDevice) - } - // /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it - params.Tmpfs(container.AbsFHSDev.Append("shm"), 0, 01777) - - /* retrieve paths and hide them if they're made available in the sandbox; - - this feature tries to improve user experience of permissive defaults, and - to warn about issues in custom configuration; it is NOT a security feature - and should not be treated as such, ALWAYS be careful with what you bind */ - var hidePaths []string - hidePaths = append(hidePaths, sc.RuntimePath.String(), sc.SharePath.String()) - _, systemBusAddr := dbus.Address() - if entries, err := dbus.Parse([]byte(systemBusAddr)); err != nil { - return nil, nil, err - } else { - // there is usually only one, do not preallocate - for _, entry := range entries { - if entry.Method != "unix" { - continue - } - for _, pair := range entry.Values { - if pair[0] == "path" { - if path.IsAbs(pair[1]) { - // get parent dir of socket - dir := path.Dir(pair[1]) - if dir == "." || dir == container.FHSRoot { - msg.Verbosef("dbus socket %q is in an unusual location", pair[1]) - } - hidePaths = append(hidePaths, dir) - } else { - msg.Verbosef("dbus socket %q is not absolute", pair[1]) - } - } - } - } - } - hidePathMatch := make([]bool, len(hidePaths)) - for i := range hidePaths { - if err := evalSymlinks(msg, k, &hidePaths[i]); err != nil { - return nil, nil, err - } - } - - var hidePathSourceCount int - for i, c := range filesystem { - if !c.Valid() { - return nil, nil, fmt.Errorf("invalid filesystem at index %d", i) - } - c.Apply(as) - - // fs counter - hidePathSourceCount += len(c.Host()) - } - - // AutoRootOp is a collection of many BindMountOp internally - var autoRootEntries []fs.DirEntry - if autoroot != nil { - if d, err := k.readdir(autoroot.Source.String()); err != nil { - return nil, nil, err - } else { - // autoroot counter - hidePathSourceCount += len(d) - autoRootEntries = d - } - } - - hidePathSource := make([]*container.Absolute, 0, hidePathSourceCount) - - // fs append - for _, c := range filesystem { - // all entries already checked above - hidePathSource = append(hidePathSource, c.Host()...) - } - - // autoroot append - if autoroot != nil { - for _, ent := range autoRootEntries { - name := ent.Name() - if container.IsAutoRootBindable(msg, name) { - hidePathSource = append(hidePathSource, autoroot.Source.Append(name)) - } - } - } - - // evaluated path, input path - hidePathSourceEval := make([][2]string, len(hidePathSource)) - for i, a := range hidePathSource { - if a == nil { - // unreachable - return nil, nil, syscall.ENOTRECOVERABLE - } - - hidePathSourceEval[i] = [2]string{a.String(), a.String()} - if err := evalSymlinks(msg, k, &hidePathSourceEval[i][0]); err != nil { - return nil, nil, err - } - } - - for _, p := range hidePathSourceEval { - for i := range hidePaths { - // skip matched entries - if hidePathMatch[i] { - continue - } - - if ok, err := deepContainsH(p[0], hidePaths[i]); err != nil { - return nil, nil, err - } else if ok { - hidePathMatch[i] = true - msg.Verbosef("hiding path %q from %q", hidePaths[i], p[1]) - } - } - } - - // cover matched paths - for i, ok := range hidePathMatch { - if ok { - if a, err := container.NewAbs(hidePaths[i]); err != nil { - var absoluteError *container.AbsoluteError - if !errors.As(err, &absoluteError) { - return nil, nil, err - } - if absoluteError == nil { - return nil, nil, syscall.ENOTRECOVERABLE - } - return nil, nil, fmt.Errorf("invalid path hiding candidate %q", absoluteError.Pathname) - } else { - params.Tmpfs(a, 1<<13, 0755) - } - } - } - - // no more ContainerConfig paths beyond this point - if !s.Device { - params.Remount(container.AbsFHSDev, syscall.MS_RDONLY) - } - - return params, maps.Clone(s.Env), nil -} - -// evalSymlinks calls syscallDispatcher.evalSymlinks but discards errors unwrapping to [fs.ErrNotExist]. -func evalSymlinks(msg container.Msg, k syscallDispatcher, v *string) error { - if p, err := k.evalSymlinks(*v); err != nil { - if !errors.Is(err, fs.ErrNotExist) { - return err - } - msg.Verbosef("path %q does not yet exist", *v) - } else { - *v = p - } - return nil -} |
