Compare commits

..

No commits in common. "749a2779f5ba8b2c575d1459db04c9209d914627" and "e6967b8bbb5ceec3abbd002f52cff1167a969e9e" have entirely different histories.

62 changed files with 440 additions and 1268 deletions

View File

@ -8,11 +8,7 @@
<p align="center"> <p align="center">
<a href="https://pkg.go.dev/hakurei.app"><img src="https://pkg.go.dev/badge/hakurei.app.svg" alt="Go Reference" /></a> <a href="https://pkg.go.dev/hakurei.app"><img src="https://pkg.go.dev/badge/hakurei.app.svg" alt="Go Reference" /></a>
<a href="https://git.gensokyo.uk/security/hakurei/actions"><img src="https://git.gensokyo.uk/security/hakurei/actions/workflows/test.yml/badge.svg?branch=staging&style=flat-square" alt="Gitea Workflow Status" /></a>
<br/>
<a href="https://git.gensokyo.uk/security/hakurei/releases"><img src="https://img.shields.io/gitea/v/release/security/hakurei?gitea_url=https%3A%2F%2Fgit.gensokyo.uk&color=purple" alt="Release" /></a>
<a href="https://goreportcard.com/report/hakurei.app"><img src="https://goreportcard.com/badge/hakurei.app" alt="Go Report Card" /></a> <a href="https://goreportcard.com/report/hakurei.app"><img src="https://goreportcard.com/badge/hakurei.app" alt="Go Report Card" /></a>
<a href="https://hakurei.app"><img src="https://img.shields.io/website?url=https%3A%2F%2Fhakurei.app" alt="Website" /></a>
</p> </p>
Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel. Hakurei is a tool for running sandboxed graphical applications as dedicated subordinate users on the Linux kernel.

View File

@ -13,11 +13,12 @@ import (
"syscall" "syscall"
"time" "time"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/app/instance"
"hakurei.app/cmd/hakurei/internal/state"
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/internal/app"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
"hakurei.app/system" "hakurei.app/system"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
@ -32,7 +33,7 @@ func buildCommand(out io.Writer) command.Command {
Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity"). Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity").
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable") Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")
c.Command("shim", command.UsageInternal, func([]string) error { app.ShimMain(); return errSuccess }) c.Command("shim", command.UsageInternal, func([]string) error { instance.ShimMain(); return errSuccess })
c.Command("app", "Load app from configuration file", func(args []string) error { c.Command("app", "Load app from configuration file", func(args []string) error {
if len(args) < 1 { if len(args) < 1 {
@ -243,14 +244,14 @@ func runApp(config *hst.Config) {
ctx, stop := signal.NotifyContext(context.Background(), ctx, stop := signal.NotifyContext(context.Background(),
syscall.SIGINT, syscall.SIGTERM) syscall.SIGINT, syscall.SIGTERM)
defer stop() // unreachable defer stop() // unreachable
a := app.MustNew(ctx, std) a := instance.MustNew(instance.ISetuid, ctx, std)
rs := new(app.RunState) rs := new(app.RunState)
if sa, err := a.Seal(config); err != nil { if sa, err := a.Seal(config); err != nil {
hlog.PrintBaseError(err, "cannot seal app:") hlog.PrintBaseError(err, "cannot seal app:")
internal.Exit(1) internal.Exit(1)
} else { } else {
internal.Exit(app.PrintRunStateErr(rs, sa.Run(rs))) internal.Exit(instance.PrintRunStateErr(instance.ISetuid, rs, sa.Run(rs)))
} }
*(*int)(nil) = 0 // not reached *(*int)(nil) = 0 // not reached

View File

@ -2,19 +2,15 @@
package app package app
import ( import (
"context"
"log"
"syscall" "syscall"
"time" "time"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/internal/sys"
) )
type App interface { type App interface {
// ID returns a copy of [ID] held by App. // ID returns a copy of [ID] held by App.
ID() state.ID ID() ID
// Seal determines the outcome of config as a [SealedApp]. // Seal determines the outcome of config as a [SealedApp].
// The value of config might be overwritten and must not be used again. // The value of config might be overwritten and must not be used again.
@ -51,11 +47,3 @@ func (rs *RunState) SetStart() {
now := time.Now().UTC() now := time.Now().UTC()
rs.Time = &now rs.Time = &now
} }
func MustNew(ctx context.Context, os sys.State) App {
a, err := New(ctx, os)
if err != nil {
log.Fatalf("cannot create app: %v", err)
}
return a
}

View File

@ -1,4 +1,4 @@
package state package app
import ( import (
"crypto/rand" "crypto/rand"

View File

@ -1,22 +1,22 @@
package state_test package app_test
import ( import (
"errors" "errors"
"testing" "testing"
"hakurei.app/internal/app/state" . "hakurei.app/cmd/hakurei/internal/app"
) )
func TestParseAppID(t *testing.T) { func TestParseAppID(t *testing.T) {
t.Run("bad length", func(t *testing.T) { t.Run("bad length", func(t *testing.T) {
if err := state.ParseAppID(new(state.ID), "meow"); !errors.Is(err, state.ErrInvalidLength) { if err := ParseAppID(new(ID), "meow"); !errors.Is(err, ErrInvalidLength) {
t.Errorf("ParseAppID: error = %v, wantErr = %v", err, state.ErrInvalidLength) t.Errorf("ParseAppID: error = %v, wantErr = %v", err, ErrInvalidLength)
} }
}) })
t.Run("bad byte", func(t *testing.T) { t.Run("bad byte", func(t *testing.T) {
wantErr := "invalid char '\\n' at byte 15" wantErr := "invalid char '\\n' at byte 15"
if err := state.ParseAppID(new(state.ID), "02bc7f8936b2af6\n\ne2535cd71ef0bb7"); err == nil || err.Error() != wantErr { if err := ParseAppID(new(ID), "02bc7f8936b2af6\n\ne2535cd71ef0bb7"); err == nil || err.Error() != wantErr {
t.Errorf("ParseAppID: error = %v, wantErr = %v", err, wantErr) t.Errorf("ParseAppID: error = %v, wantErr = %v", err, wantErr)
} }
}) })
@ -30,30 +30,30 @@ func TestParseAppID(t *testing.T) {
func FuzzParseAppID(f *testing.F) { func FuzzParseAppID(f *testing.F) {
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
id := new(state.ID) id := new(ID)
if err := state.NewAppID(id); err != nil { if err := NewAppID(id); err != nil {
panic(err.Error()) panic(err.Error())
} }
f.Add(id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]) f.Add(id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15])
} }
f.Fuzz(func(t *testing.T, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 byte) { f.Fuzz(func(t *testing.T, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 byte) {
testParseAppID(t, &state.ID{b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}) testParseAppID(t, &ID{b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15})
}) })
} }
func testParseAppIDWithRandom(t *testing.T) { func testParseAppIDWithRandom(t *testing.T) {
id := new(state.ID) id := new(ID)
if err := state.NewAppID(id); err != nil { if err := NewAppID(id); err != nil {
t.Fatalf("cannot generate app ID: %v", err) t.Fatalf("cannot generate app ID: %v", err)
} }
testParseAppID(t, id) testParseAppID(t, id)
} }
func testParseAppID(t *testing.T, id *state.ID) { func testParseAppID(t *testing.T, id *ID) {
s := id.String() s := id.String()
got := new(state.ID) got := new(ID)
if err := state.ParseAppID(got, s); err != nil { if err := ParseAppID(got, s); err != nil {
t.Fatalf("cannot parse app ID: %v", err) t.Fatalf("cannot parse app ID: %v", err)
} }

View File

@ -1,4 +1,4 @@
package app package common
import ( import (
"errors" "errors"
@ -19,9 +19,9 @@ import (
// allocating slightly more as a margin for future expansion // allocating slightly more as a margin for future expansion
const preallocateOpsCount = 1 << 5 const preallocateOpsCount = 1 << 5
// newContainer initialises [container.Params] via [hst.ContainerConfig]. // NewContainer initialises [sandbox.Params] via [hst.ContainerConfig].
// Note that remaining container setup must be queued by the caller. // Note that remaining container setup must be queued by the caller.
func newContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*container.Params, map[string]string, error) { func NewContainer(s *hst.ContainerConfig, os sys.State, uid, gid *int) (*container.Params, map[string]string, error) {
if s == nil { if s == nil {
return nil, nil, syscall.EBADE return nil, nil, syscall.EBADE
} }

View File

@ -1,4 +1,4 @@
package app package common
import ( import (
"path/filepath" "path/filepath"

View File

@ -1,4 +1,4 @@
package app package common
import ( import (
"testing" "testing"

View File

@ -0,0 +1,17 @@
package instance
import (
"syscall"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/app/internal/setuid"
)
func PrintRunStateErr(whence int, rs *app.RunState, runErr error) (code int) {
switch whence {
case ISetuid:
return setuid.PrintRunStateErr(rs, runErr)
default:
panic(syscall.EINVAL)
}
}

View File

@ -0,0 +1,33 @@
// Package instance exposes cross-package implementation details and provides constructors for builtin implementations.
package instance
import (
"context"
"log"
"syscall"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/app/internal/setuid"
"hakurei.app/internal/sys"
)
const (
ISetuid = iota
)
func New(whence int, ctx context.Context, os sys.State) (app.App, error) {
switch whence {
case ISetuid:
return setuid.New(ctx, os)
default:
return nil, syscall.EINVAL
}
}
func MustNew(whence int, ctx context.Context, os sys.State) app.App {
a, err := New(whence, ctx, os)
if err != nil {
log.Fatalf("cannot create app: %v", err)
}
return a
}

View File

@ -0,0 +1,6 @@
package instance
import "hakurei.app/cmd/hakurei/internal/app/internal/setuid"
// ShimMain is the main function of the shim process and runs as the unconstrained target user.
func ShimMain() { setuid.ShimMain() }

View File

@ -1,12 +1,12 @@
package app package setuid
import ( import (
"context" "context"
"fmt" "fmt"
"sync" "sync"
. "hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
"hakurei.app/internal/sys" "hakurei.app/internal/sys"
) )
@ -16,15 +16,15 @@ func New(ctx context.Context, os sys.State) (App, error) {
a.sys = os a.sys = os
a.ctx = ctx a.ctx = ctx
id := new(state.ID) id := new(ID)
err := state.NewAppID(id) err := NewAppID(id)
a.id = newID(id) a.id = newID(id)
return a, err return a, err
} }
type app struct { type app struct {
id *stringPair[state.ID] id *stringPair[ID]
sys sys.State sys sys.State
ctx context.Context ctx context.Context
@ -32,7 +32,7 @@ type app struct {
mu sync.RWMutex mu sync.RWMutex
} }
func (a *app) ID() state.ID { a.mu.RLock(); defer a.mu.RUnlock(); return a.id.unwrap() } func (a *app) ID() ID { a.mu.RLock(); defer a.mu.RUnlock(); return a.id.unwrap() }
func (a *app) String() string { func (a *app) String() string {
if a == nil { if a == nil {

View File

@ -1,10 +1,10 @@
package app_test package setuid_test
import ( import (
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/system" "hakurei.app/system"
"hakurei.app/system/acl" "hakurei.app/system/acl"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
@ -52,7 +52,7 @@ var testCasesNixos = []sealTestCase{
Data: "/var/lib/persist/module/hakurei/0/1", Data: "/var/lib/persist/module/hakurei/0/1",
Identity: 1, Groups: []string{}, Identity: 1, Groups: []string{},
}, },
state.ID{ app.ID{
0x8e, 0x2c, 0x76, 0xb0, 0x8e, 0x2c, 0x76, 0xb0,
0x66, 0xda, 0xbe, 0x57, 0x66, 0xda, 0xbe, 0x57,
0x4c, 0xf0, 0x73, 0xbd, 0x4c, 0xf0, 0x73, 0xbd,

View File

@ -1,12 +1,12 @@
package app_test package setuid_test
import ( import (
"os" "os"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/system" "hakurei.app/system"
"hakurei.app/system/acl" "hakurei.app/system/acl"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
@ -16,7 +16,7 @@ var testCasesPd = []sealTestCase{
{ {
"nixos permissive defaults no enablements", new(stubNixOS), "nixos permissive defaults no enablements", new(stubNixOS),
&hst.Config{Username: "chronos", Data: "/home/chronos"}, &hst.Config{Username: "chronos", Data: "/home/chronos"},
state.ID{ app.ID{
0x4a, 0x45, 0x0b, 0x65, 0x4a, 0x45, 0x0b, 0x65,
0x96, 0xd7, 0xbc, 0x15, 0x96, 0xd7, 0xbc, 0x15,
0xbd, 0x01, 0x78, 0x0e, 0xbd, 0x01, 0x78, 0x0e,
@ -115,7 +115,7 @@ var testCasesPd = []sealTestCase{
}, },
Enablements: system.EWayland | system.EDBus | system.EPulse, Enablements: system.EWayland | system.EDBus | system.EPulse,
}, },
state.ID{ app.ID{
0xeb, 0xf0, 0x83, 0xd1, 0xeb, 0xf0, 0x83, 0xd1,
0xb1, 0x75, 0x91, 0x17, 0xb1, 0x75, 0x91, 0x17,
0x82, 0xd4, 0x13, 0x36, 0x82, 0xd4, 0x13, 0x36,

View File

@ -1,4 +1,4 @@
package app_test package setuid_test
import ( import (
"fmt" "fmt"

View File

@ -1,4 +1,4 @@
package app_test package setuid_test
import ( import (
"encoding/json" "encoding/json"
@ -7,10 +7,10 @@ import (
"testing" "testing"
"time" "time"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/app/internal/setuid"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app"
"hakurei.app/internal/app/state"
"hakurei.app/internal/sys" "hakurei.app/internal/sys"
"hakurei.app/system" "hakurei.app/system"
) )
@ -19,7 +19,7 @@ type sealTestCase struct {
name string name string
os sys.State os sys.State
config *hst.Config config *hst.Config
id state.ID id app.ID
wantSys *system.I wantSys *system.I
wantContainer *container.Params wantContainer *container.Params
} }
@ -29,7 +29,7 @@ func TestApp(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
a := app.NewWithID(tc.id, tc.os) a := setuid.NewWithID(tc.id, tc.os)
var ( var (
gotSys *system.I gotSys *system.I
gotContainer *container.Params gotContainer *container.Params
@ -39,7 +39,7 @@ func TestApp(t *testing.T) {
t.Errorf("Seal: error = %v", err) t.Errorf("Seal: error = %v", err)
return return
} else { } else {
gotSys, gotContainer = app.AppIParams(a, sa) gotSys, gotContainer = setuid.AppIParams(a, sa)
} }
}) { }) {
return return

View File

@ -1,9 +1,10 @@
package app package setuid
import ( import (
"errors" "errors"
"log" "log"
. "hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
) )

View File

@ -1,13 +1,13 @@
package app package setuid
import ( import (
. "hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/internal/app/state"
"hakurei.app/internal/sys" "hakurei.app/internal/sys"
"hakurei.app/system" "hakurei.app/system"
) )
func NewWithID(id state.ID, os sys.State) App { func NewWithID(id ID, os sys.State) App {
a := new(app) a := new(app)
a.id = newID(&id) a.id = newID(&id)
a.sys = os a.sys = os

View File

@ -1,4 +1,4 @@
package app package setuid
import ( import (
"context" "context"
@ -12,9 +12,10 @@ import (
"syscall" "syscall"
"time" "time"
. "hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/state"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
"hakurei.app/system" "hakurei.app/system"
) )

View File

@ -1,4 +1,4 @@
package app package setuid
import ( import (
"bytes" "bytes"
@ -16,10 +16,11 @@ import (
"sync/atomic" "sync/atomic"
"syscall" "syscall"
. "hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/app/instance/common"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
"hakurei.app/internal/sys" "hakurei.app/internal/sys"
"hakurei.app/system" "hakurei.app/system"
@ -65,7 +66,7 @@ var posixUsername = regexp.MustCompilePOSIX("^[a-z_]([A-Za-z0-9_-]{0,31}|[A-Za-z
// outcome stores copies of various parts of [hst.Config] // outcome stores copies of various parts of [hst.Config]
type outcome struct { type outcome struct {
// copied from initialising [app] // copied from initialising [app]
id *stringPair[state.ID] id *stringPair[ID]
// copied from [sys.State] response // copied from [sys.State] response
runDirPath string runDirPath string
@ -280,7 +281,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
{ {
var uid, gid int var uid, gid int
var err error var err error
seal.container, seal.env, err = newContainer(config.Container, sys, &uid, &gid) seal.container, seal.env, err = common.NewContainer(config.Container, sys, &uid, &gid)
if err != nil { if err != nil {
return hlog.WrapErrSuffix(err, return hlog.WrapErrSuffix(err,
"cannot initialise container configuration:") "cannot initialise container configuration:")

View File

@ -1,4 +1,4 @@
package app package setuid
import ( import (
"context" "context"

View File

@ -1,13 +1,13 @@
package app package setuid
import ( import (
"strconv" "strconv"
"hakurei.app/internal/app/state" . "hakurei.app/cmd/hakurei/internal/app"
) )
func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} } func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} }
func newID(id *state.ID) *stringPair[state.ID] { return &stringPair[state.ID]{*id, id.String()} } func newID(id *ID) *stringPair[ID] { return &stringPair[ID]{*id, id.String()} }
// stringPair stores a value and its string representation. // stringPair stores a value and its string representation.
type stringPair[T comparable] struct { type stringPair[T comparable] struct {

View File

@ -13,6 +13,7 @@ import (
"sync" "sync"
"syscall" "syscall"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
) )
@ -129,7 +130,7 @@ type multiBackend struct {
lock sync.RWMutex lock sync.RWMutex
} }
func (b *multiBackend) filename(id *ID) string { func (b *multiBackend) filename(id *app.ID) string {
return path.Join(b.path, id.String()) return path.Join(b.path, id.String())
} }
@ -189,8 +190,8 @@ func (b *multiBackend) load(decode bool) (Entries, error) {
return nil, fmt.Errorf("unexpected directory %q in store", e.Name()) return nil, fmt.Errorf("unexpected directory %q in store", e.Name())
} }
id := new(ID) id := new(app.ID)
if err := ParseAppID(id, e.Name()); err != nil { if err := app.ParseAppID(id, e.Name()); err != nil {
return nil, err return nil, err
} }
@ -335,7 +336,7 @@ func (b *multiBackend) encodeState(w io.WriteSeeker, state *State, configWriter
return err return err
} }
func (b *multiBackend) Destroy(id ID) error { func (b *multiBackend) Destroy(id app.ID) error {
b.lock.Lock() b.lock.Lock()
defer b.lock.Unlock() defer b.lock.Unlock()

View File

@ -3,7 +3,7 @@ package state_test
import ( import (
"testing" "testing"
"hakurei.app/internal/app/state" "hakurei.app/cmd/hakurei/internal/state"
) )
func TestMulti(t *testing.T) { testStore(t, state.NewMulti(t.TempDir())) } func TestMulti(t *testing.T) { testStore(t, state.NewMulti(t.TempDir())) }

View File

@ -1,4 +1,3 @@
// Package state provides cross-process state tracking for hakurei container instances.
package state package state
import ( import (
@ -6,12 +5,13 @@ import (
"io" "io"
"time" "time"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/hst" "hakurei.app/hst"
) )
var ErrNoConfig = errors.New("state does not contain config") var ErrNoConfig = errors.New("state does not contain config")
type Entries map[ID]*State type Entries map[app.ID]*State
type Store interface { type Store interface {
// Do calls f exactly once and ensures store exclusivity until f returns. // Do calls f exactly once and ensures store exclusivity until f returns.
@ -30,7 +30,7 @@ type Store interface {
// Cursor provides access to the store // Cursor provides access to the store
type Cursor interface { type Cursor interface {
Save(state *State, configWriter io.WriterTo) error Save(state *State, configWriter io.WriterTo) error
Destroy(id ID) error Destroy(id app.ID) error
Load() (Entries, error) Load() (Entries, error)
Len() (int, error) Len() (int, error)
} }
@ -38,7 +38,7 @@ type Cursor interface {
// State is an instance state // State is an instance state
type State struct { type State struct {
// hakurei instance id // hakurei instance id
ID ID `json:"instance"` ID app.ID `json:"instance"`
// child process PID value // child process PID value
PID int `json:"pid"` PID int `json:"pid"`
// sealed app configuration // sealed app configuration

View File

@ -10,8 +10,9 @@ import (
"testing" "testing"
"time" "time"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/state"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
) )
func testStore(t *testing.T, s state.Store) { func testStore(t *testing.T, s state.Store) {
@ -133,7 +134,7 @@ func testStore(t *testing.T, s state.Store) {
} }
func makeState(t *testing.T, s *state.State, ct io.Writer) { func makeState(t *testing.T, s *state.State, ct io.Writer) {
if err := state.NewAppID(&s.ID); err != nil { if err := app.NewAppID(&s.ID); err != nil {
t.Fatalf("cannot create dummy state: %v", err) t.Fatalf("cannot create dummy state: %v", err)
} }
if err := gob.NewEncoder(ct).Encode(hst.Template()); err != nil { if err := gob.NewEncoder(ct).Encode(hst.Template()); err != nil {

View File

@ -10,8 +10,8 @@ import (
"strings" "strings"
"syscall" "syscall"
"hakurei.app/cmd/hakurei/internal/state"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
) )

View File

@ -12,8 +12,8 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"hakurei.app/cmd/hakurei/internal/state"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/internal/hlog" "hakurei.app/internal/hlog"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
) )

View File

@ -5,13 +5,14 @@ import (
"testing" "testing"
"time" "time"
"hakurei.app/cmd/hakurei/internal/app"
"hakurei.app/cmd/hakurei/internal/state"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
) )
var ( var (
testID = state.ID{ testID = app.ID{
0x8e, 0x2c, 0x76, 0xb0, 0x8e, 0x2c, 0x76, 0xb0,
0x66, 0xda, 0xbe, 0x57, 0x66, 0xda, 0xbe, 0x57,
0x4c, 0xf0, 0x73, 0xbd, 0x4c, 0xf0, 0x73, 0xbd,
@ -459,7 +460,7 @@ func Test_printPs(t *testing.T) {
{"no entries", make(state.Entries), false, false, " Instance PID Application Uptime\n"}, {"no entries", make(state.Entries), false, false, " Instance PID Application Uptime\n"},
{"no entries short", make(state.Entries), true, false, ""}, {"no entries short", make(state.Entries), true, false, ""},
{"nil instance", state.Entries{testID: nil}, false, false, " Instance PID Application Uptime\n"}, {"nil instance", state.Entries{testID: nil}, false, false, " Instance PID Application Uptime\n"},
{"state corruption", state.Entries{state.ID{}: testState}, false, false, " Instance PID Application Uptime\n"}, {"state corruption", state.Entries{app.ID{}: testState}, false, false, " Instance PID Application Uptime\n"},
{"valid pd", state.Entries{testID: &state.State{ID: testID, PID: 1 << 8, Config: new(hst.Config), Time: testAppTime}}, false, false, ` Instance PID Application Uptime {"valid pd", state.Entries{testID: &state.State{ID: testID, PID: 1 << 8, Config: new(hst.Config), Time: testAppTime}}, false, false, ` Instance PID Application Uptime
8e2c76b0 256 0 (app.hakurei.8e2c76b0) 1h2m32s 8e2c76b0 256 0 (app.hakurei.8e2c76b0) 1h2m32s

View File

@ -1,45 +0,0 @@
package container
import (
"syscall"
"unsafe"
)
const (
_LINUX_CAPABILITY_VERSION_3 = 0x20080522
PR_CAP_AMBIENT = 0x2f
PR_CAP_AMBIENT_RAISE = 0x2
PR_CAP_AMBIENT_CLEAR_ALL = 0x4
CAP_SYS_ADMIN = 0x15
CAP_SETPCAP = 0x8
)
type (
capHeader struct {
version uint32
pid int32
}
capData struct {
effective uint32
permitted uint32
inheritable uint32
}
)
// See CAP_TO_INDEX in linux/capability.h:
func capToIndex(cap uintptr) uintptr { return cap >> 5 }
// See CAP_TO_MASK in linux/capability.h:
func capToMask(cap uintptr) uint32 { return 1 << uint(cap&31) }
func capset(hdrp *capHeader, datap *[2]capData) error {
if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET,
uintptr(unsafe.Pointer(hdrp)),
uintptr(unsafe.Pointer(&datap[0])), 0); errno != 0 {
return errno
}
return nil
}

View File

@ -20,21 +20,8 @@ const (
// time to wait for linger processes after death of initial process // time to wait for linger processes after death of initial process
residualProcessTimeout = 5 * time.Second residualProcessTimeout = 5 * time.Second
/* intermediate tmpfs mount point // intermediate tmpfs mount point
basePath = "/tmp"
this path might seem like a weird choice, however there are many good reasons to use it:
- the contents of this path is never exposed to the container:
the tmpfs root established here effectively becomes anonymous after pivot_root
- it is safe to assume this path exists and is a directory:
this program will not work correctly without a proper /proc and neither will most others
- this path belongs to the container init:
the container init is not any more privileged or trusted than the rest of the container
- this path is only accessible by init and root:
the container init sets SUID_DUMP_DISABLE and terminates if that fails;
it should be noted that none of this should become relevant at any point since the resulting
intermediate root tmpfs should be effectively anonymous */
intermediateHostPath = "/proc/self/fd"
// setup params file descriptor // setup params file descriptor
setupEnv = "HAKUREI_SETUP" setupEnv = "HAKUREI_SETUP"
@ -137,10 +124,10 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
} }
if err := Mount("rootfs", intermediateHostPath, "tmpfs", MS_NODEV|MS_NOSUID, ""); err != nil { if err := Mount("rootfs", basePath, "tmpfs", MS_NODEV|MS_NOSUID, ""); err != nil {
log.Fatalf("cannot mount intermediate root: %v", err) log.Fatalf("cannot mount intermediate root: %v", err)
} }
if err := os.Chdir(intermediateHostPath); err != nil { if err := os.Chdir(basePath); err != nil {
log.Fatalf("cannot enter base path: %v", err) log.Fatalf("cannot enter base path: %v", err)
} }
@ -154,8 +141,8 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
if err := os.Mkdir(hostDir, 0755); err != nil { if err := os.Mkdir(hostDir, 0755); err != nil {
log.Fatalf("%v", err) log.Fatalf("%v", err)
} }
// pivot_root uncovers intermediateHostPath in hostDir // pivot_root uncovers basePath in hostDir
if err := PivotRoot(intermediateHostPath, hostDir); err != nil { if err := PivotRoot(basePath, hostDir); err != nil {
log.Fatalf("cannot pivot into intermediate root: %v", err) log.Fatalf("cannot pivot into intermediate root: %v", err)
} }
if err := os.Chdir("/"); err != nil { if err := os.Chdir("/"); err != nil {
@ -211,7 +198,7 @@ func Init(prepare func(prefix string), setVerbose func(verbose bool)) {
} }
} }
if _, _, errno := Syscall(SYS_PRCTL, PR_SET_NO_NEW_PRIVS, 1, 0); errno != 0 { if _, _, errno := Syscall(PR_SET_NO_NEW_PRIVS, 1, 0, 0); errno != 0 {
log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno) log.Fatalf("prctl(PR_SET_NO_NEW_PRIVS): %v", errno)
} }

View File

@ -1,24 +0,0 @@
package seccomp_test
import . "hakurei.app/container/seccomp"
var bpfExpected = bpfLookup{
{AllowMultiarch | AllowCAN |
AllowBluetooth, PresetExt |
PresetDenyNS | PresetDenyTTY | PresetDenyDevel |
PresetLinux32}: toHash(
"e99dd345e195413473d3cbee07b4ed57b908bfa89ea2072fe93482847f50b5b758da17e74ca2bbc00813de49a2b9bf834c024ed48850be69b68a9a4c5f53a9db"),
{0, 0}: toHash(
"95ec69d017733e072160e0da80fdebecdf27ae8166f5e2a731270c98ea2d2946cb5231029063668af215879155da21aca79b070e04c0ee9acdf58f55cfa815a5"),
{0, PresetExt}: toHash(
"dc7f2e1c5e829b79ebb7efc759150f54a83a75c8df6fee4dce5dadc4736c585d4deebfeb3c7969af3a077e90b77bb4741db05d90997c8659b95891206ac9952d"),
{0, PresetStrict}: toHash(
"e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735"),
{0, PresetDenyNS | PresetDenyTTY | PresetDenyDevel}: toHash(
"39871b93ffafc8b979fcedc0b0c37b9e03922f5b02748dc5c3c17c92527f6e022ede1f48bff59246ea452c0d1de54827808b1a6f84f32bbde1aa02ae30eedcfa"),
{0, PresetExt | PresetDenyDevel}: toHash(
"c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a"),
{0, PresetExt | PresetDenyNS | PresetDenyDevel}: toHash(
"0b76007476c1c9e25dbf674c29fdf609a1656a70063e49327654e1b5360ad3da06e1a3e32bf80e961c5516ad83d4b9e7e9bde876a93797e27627d2555c25858b"),
}

View File

@ -1,24 +0,0 @@
package seccomp_test
import . "hakurei.app/container/seccomp"
var bpfExpected = bpfLookup{
{AllowMultiarch | AllowCAN |
AllowBluetooth, PresetExt |
PresetDenyNS | PresetDenyTTY | PresetDenyDevel |
PresetLinux32}: toHash(
"1431c013f2ddac3adae577821cb5d351b1514e7c754d62346ddffd31f46ea02fb368e46e3f8104f81019617e721fe687ddd83f1e79580622ccc991da12622170"),
{0, 0}: toHash(
"450c21210dbf124dfa7ae56d0130f9c2e24b26f5bce8795ee75766c75850438ff9e7d91c5e73d63bbe51a5d4b06c2a0791c4de2903b2b9805f16265318183235"),
{0, PresetExt}: toHash(
"d971d0f2d30f54ac920fc6d84df2be279e9fd28cf2d48be775d7fdbd790b750e1369401cd3bb8bcf9ba3adb91874fe9792d9e3f62209b8ee59c9fdd2ddd10c7b"),
{0, PresetStrict}: toHash(
"79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297"),
{0, PresetDenyNS | PresetDenyTTY | PresetDenyDevel}: toHash(
"228286c2f5df8e44463be0a57b91977b7f38b63b09e5d98dfabe5c61545b8f9ac3e5ea3d86df55d7edf2ce61875f0a5a85c0ab82800bef178c42533e8bdc9a6c"),
{0, PresetExt | PresetDenyDevel}: toHash(
"433ce9b911282d6dcc8029319fb79b816b60d5a795ec8fc94344dd027614d68f023166a91bb881faaeeedd26e3d89474e141e5a69a97e93b8984ca8f14999980"),
{0, PresetExt | PresetDenyNS | PresetDenyDevel}: toHash(
"cf1f4dc87436ba8ec95d268b663a6397bb0b4a5ac64d8557e6cc529d8b0f6f65dad3a92b62ed29d85eee9c6dde1267757a4d0f86032e8a45ca1bceadfa34cf5e"),
}

View File

@ -1,28 +0,0 @@
package seccomp_test
import (
"encoding/hex"
"hakurei.app/container/seccomp"
)
type (
bpfPreset = struct {
seccomp.ExportFlag
seccomp.FilterPreset
}
bpfLookup map[bpfPreset][]byte
)
func toHash(s string) []byte {
if len(s) != 128 {
panic("bad sha512 string length")
}
if v, err := hex.DecodeString(s); err != nil {
panic(err.Error())
} else if len(v) != 64 {
panic("unreachable")
} else {
return v
}
}

View File

@ -4,7 +4,6 @@ package seccomp
#cgo linux pkg-config: --static libseccomp #cgo linux pkg-config: --static libseccomp
#include <libseccomp-helper.h> #include <libseccomp-helper.h>
#include <sys/personality.h>
*/ */
import "C" import "C"
import ( import (
@ -15,11 +14,6 @@ import (
"unsafe" "unsafe"
) )
const (
PER_LINUX = C.PER_LINUX
PER_LINUX32 = C.PER_LINUX32
)
var ( var (
ErrInvalidRules = errors.New("invalid native rules slice") ErrInvalidRules = errors.New("invalid native rules slice")
) )

View File

@ -14,28 +14,81 @@ import (
func TestExport(t *testing.T) { func TestExport(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
flags ExportFlag
presets FilterPreset presets FilterPreset
flags ExportFlag
want []byte
wantErr bool wantErr bool
}{ }{
{"everything", AllowMultiarch | AllowCAN | {"compat", 0, 0, []byte{
AllowBluetooth, PresetExt | 0x95, 0xec, 0x69, 0xd0, 0x17, 0x73, 0x3e, 0x07,
0x21, 0x60, 0xe0, 0xda, 0x80, 0xfd, 0xeb, 0xec,
0xdf, 0x27, 0xae, 0x81, 0x66, 0xf5, 0xe2, 0xa7,
0x31, 0x27, 0x0c, 0x98, 0xea, 0x2d, 0x29, 0x46,
0xcb, 0x52, 0x31, 0x02, 0x90, 0x63, 0x66, 0x8a,
0xf2, 0x15, 0x87, 0x91, 0x55, 0xda, 0x21, 0xac,
0xa7, 0x9b, 0x07, 0x0e, 0x04, 0xc0, 0xee, 0x9a,
0xcd, 0xf5, 0x8f, 0x55, 0xcf, 0xa8, 0x15, 0xa5,
}, false},
{"base", PresetExt, 0, []byte{
0xdc, 0x7f, 0x2e, 0x1c, 0x5e, 0x82, 0x9b, 0x79,
0xeb, 0xb7, 0xef, 0xc7, 0x59, 0x15, 0x0f, 0x54,
0xa8, 0x3a, 0x75, 0xc8, 0xdf, 0x6f, 0xee, 0x4d,
0xce, 0x5d, 0xad, 0xc4, 0x73, 0x6c, 0x58, 0x5d,
0x4d, 0xee, 0xbf, 0xeb, 0x3c, 0x79, 0x69, 0xaf,
0x3a, 0x07, 0x7e, 0x90, 0xb7, 0x7b, 0xb4, 0x74,
0x1d, 0xb0, 0x5d, 0x90, 0x99, 0x7c, 0x86, 0x59,
0xb9, 0x58, 0x91, 0x20, 0x6a, 0xc9, 0x95, 0x2d,
}, false},
{"everything", PresetExt |
PresetDenyNS | PresetDenyTTY | PresetDenyDevel | PresetDenyNS | PresetDenyTTY | PresetDenyDevel |
PresetLinux32, false}, PresetLinux32, AllowMultiarch | AllowCAN |
AllowBluetooth, []byte{
{"compat", 0, 0, false}, 0xe9, 0x9d, 0xd3, 0x45, 0xe1, 0x95, 0x41, 0x34,
{"base", 0, PresetExt, false}, 0x73, 0xd3, 0xcb, 0xee, 0x07, 0xb4, 0xed, 0x57,
{"strict", 0, PresetStrict, false}, 0xb9, 0x08, 0xbf, 0xa8, 0x9e, 0xa2, 0x07, 0x2f,
{"strict compat", 0, PresetDenyNS | PresetDenyTTY | PresetDenyDevel, false}, 0xe9, 0x34, 0x82, 0x84, 0x7f, 0x50, 0xb5, 0xb7,
{"hakurei default", 0, PresetExt | PresetDenyDevel, false}, 0x58, 0xda, 0x17, 0xe7, 0x4c, 0xa2, 0xbb, 0xc0,
{"hakurei tty", 0, PresetExt | PresetDenyNS | PresetDenyDevel, false}, 0x08, 0x13, 0xde, 0x49, 0xa2, 0xb9, 0xbf, 0x83,
0x4c, 0x02, 0x4e, 0xd4, 0x88, 0x50, 0xbe, 0x69,
0xb6, 0x8a, 0x9a, 0x4c, 0x5f, 0x53, 0xa9, 0xdb,
}, false},
{"strict", PresetStrict, 0, []byte{
0xe8, 0x80, 0x29, 0x8d, 0xf2, 0xbd, 0x67, 0x51,
0xd0, 0x04, 0x0f, 0xc2, 0x1b, 0xc0, 0xed, 0x4c,
0x00, 0xf9, 0x5d, 0xc0, 0xd7, 0xba, 0x50, 0x6c,
0x24, 0x4d, 0x8b, 0x8c, 0xf6, 0x86, 0x6d, 0xba,
0x8e, 0xf4, 0xa3, 0x32, 0x96, 0xf2, 0x87, 0xb6,
0x6c, 0xcc, 0xc1, 0xd7, 0x8e, 0x97, 0x02, 0x65,
0x97, 0xf8, 0x4c, 0xc7, 0xde, 0xc1, 0x57, 0x3e,
0x14, 0x89, 0x60, 0xfb, 0xd3, 0x5c, 0xd7, 0x35,
}, false},
{"strict compat", 0 |
PresetDenyNS | PresetDenyTTY | PresetDenyDevel, 0, []byte{
0x39, 0x87, 0x1b, 0x93, 0xff, 0xaf, 0xc8, 0xb9,
0x79, 0xfc, 0xed, 0xc0, 0xb0, 0xc3, 0x7b, 0x9e,
0x03, 0x92, 0x2f, 0x5b, 0x02, 0x74, 0x8d, 0xc5,
0xc3, 0xc1, 0x7c, 0x92, 0x52, 0x7f, 0x6e, 0x02,
0x2e, 0xde, 0x1f, 0x48, 0xbf, 0xf5, 0x92, 0x46,
0xea, 0x45, 0x2c, 0x0d, 0x1d, 0xe5, 0x48, 0x27,
0x80, 0x8b, 0x1a, 0x6f, 0x84, 0xf3, 0x2b, 0xbd,
0xe1, 0xaa, 0x02, 0xae, 0x30, 0xee, 0xdc, 0xfa,
}, false},
{"hakurei default", PresetExt | PresetDenyDevel, 0, []byte{
0xc6, 0x98, 0xb0, 0x81, 0xff, 0x95, 0x7a, 0xfe,
0x17, 0xa6, 0xd9, 0x43, 0x74, 0x53, 0x7d, 0x37,
0xf2, 0xa6, 0x3f, 0x6f, 0x9d, 0xd7, 0x5d, 0xa7,
0x54, 0x65, 0x42, 0x40, 0x7a, 0x9e, 0x32, 0x47,
0x6e, 0xbd, 0xa3, 0x31, 0x2b, 0xa7, 0x78, 0x5d,
0x7f, 0x61, 0x85, 0x42, 0xbc, 0xfa, 0xf2, 0x7c,
0xa2, 0x7d, 0xcc, 0x2d, 0xdd, 0xba, 0x85, 0x20,
0x69, 0xd2, 0x8b, 0xcf, 0xe8, 0xca, 0xd3, 0x9a,
}, false},
} }
buf := make([]byte, 8) buf := make([]byte, 8)
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
e := New(Preset(tc.presets, tc.flags), tc.flags) e := New(Preset(tc.presets, tc.flags), tc.flags)
want := bpfExpected[bpfPreset{tc.flags, tc.presets}]
digest := sha512.New() digest := sha512.New()
if _, err := io.CopyBuffer(digest, e, buf); (err != nil) != tc.wantErr { if _, err := io.CopyBuffer(digest, e, buf); (err != nil) != tc.wantErr {
@ -45,9 +98,9 @@ func TestExport(t *testing.T) {
if err := e.Close(); err != nil { if err := e.Close(); err != nil {
t.Errorf("Close: error = %v", err) t.Errorf("Close: error = %v", err)
} }
if got := digest.Sum(nil); !slices.Equal(got, want) { if got := digest.Sum(nil); !slices.Equal(got, tc.want) {
t.Fatalf("Export() hash = %x, want %x", t.Fatalf("Export() hash = %x, want %x",
got, want) got, tc.want)
return return
} }
}) })

View File

@ -4,14 +4,8 @@
# license that can be found in the LICENSE file. # license that can be found in the LICENSE file.
use strict; use strict;
use POSIX ();
my $command = "mksysnum_linux.pl ". join(' ', @ARGV); my $command = "mksysnum_linux.pl ". join(' ', @ARGV);
my $uname_arch = (POSIX::uname)[4];
my %syscall_cutoff_arch = (
"x86_64" => 302,
"aarch64" => 281,
);
print <<EOF; print <<EOF;
// $command // $command
@ -36,7 +30,7 @@ sub fmt {
} }
(my $name_upper = $name) =~ y/a-z/A-Z/; (my $name_upper = $name) =~ y/a-z/A-Z/;
$num = $num + $offset; $num = $num + $offset;
if($num > $syscall_cutoff_arch{$uname_arch}){ # not wired in Go standard library if($num > 302){ # not wired in Go standard library
if($state < 0){ if($state < 0){
print " \"$name\": SYS_$name_upper,\n"; print " \"$name\": SYS_$name_upper,\n";
} }

View File

@ -4,9 +4,15 @@ package seccomp
#cgo linux pkg-config: --static libseccomp #cgo linux pkg-config: --static libseccomp
#include <seccomp.h> #include <seccomp.h>
#include <sys/personality.h>
*/ */
import "C" import "C"
const (
PER_LINUX = C.PER_LINUX
PER_LINUX32 = C.PER_LINUX32
)
var syscallNumExtra = map[string]int{ var syscallNumExtra = map[string]int{
"umount": SYS_UMOUNT, "umount": SYS_UMOUNT,
"subpage_prot": SYS_SUBPAGE_PROT, "subpage_prot": SYS_SUBPAGE_PROT,

View File

@ -1,61 +0,0 @@
package seccomp
/*
#cgo linux pkg-config: --static libseccomp
#include <seccomp.h>
*/
import "C"
import "syscall"
const (
SYS_NEWFSTATAT = syscall.SYS_FSTATAT
)
var syscallNumExtra = map[string]int{
"uselib": SYS_USELIB,
"clock_adjtime64": SYS_CLOCK_ADJTIME64,
"clock_settime64": SYS_CLOCK_SETTIME64,
"umount": SYS_UMOUNT,
"chown": SYS_CHOWN,
"chown32": SYS_CHOWN32,
"fchown32": SYS_FCHOWN32,
"lchown": SYS_LCHOWN,
"lchown32": SYS_LCHOWN32,
"setgid32": SYS_SETGID32,
"setgroups32": SYS_SETGROUPS32,
"setregid32": SYS_SETREGID32,
"setresgid32": SYS_SETRESGID32,
"setresuid32": SYS_SETRESUID32,
"setreuid32": SYS_SETREUID32,
"setuid32": SYS_SETUID32,
"modify_ldt": SYS_MODIFY_LDT,
"subpage_prot": SYS_SUBPAGE_PROT,
"switch_endian": SYS_SWITCH_ENDIAN,
"vm86": SYS_VM86,
"vm86old": SYS_VM86OLD,
}
const (
SYS_USELIB = C.__SNR_uselib
SYS_CLOCK_ADJTIME64 = C.__SNR_clock_adjtime64
SYS_CLOCK_SETTIME64 = C.__SNR_clock_settime64
SYS_UMOUNT = C.__SNR_umount
SYS_CHOWN = C.__SNR_chown
SYS_CHOWN32 = C.__SNR_chown32
SYS_FCHOWN32 = C.__SNR_fchown32
SYS_LCHOWN = C.__SNR_lchown
SYS_LCHOWN32 = C.__SNR_lchown32
SYS_SETGID32 = C.__SNR_setgid32
SYS_SETGROUPS32 = C.__SNR_setgroups32
SYS_SETREGID32 = C.__SNR_setregid32
SYS_SETRESGID32 = C.__SNR_setresgid32
SYS_SETRESUID32 = C.__SNR_setresuid32
SYS_SETREUID32 = C.__SNR_setreuid32
SYS_SETUID32 = C.__SNR_setuid32
SYS_MODIFY_LDT = C.__SNR_modify_ldt
SYS_SUBPAGE_PROT = C.__SNR_subpage_prot
SYS_SWITCH_ENDIAN = C.__SNR_switch_endian
SYS_VM86 = C.__SNR_vm86
SYS_VM86OLD = C.__SNR_vm86old
)

View File

@ -1,382 +0,0 @@
// mksysnum_linux.pl /usr/include/asm/unistd_64.h
// Code generated by the command above; DO NOT EDIT.
package seccomp
import . "syscall"
var syscallNum = map[string]int{
"io_setup": SYS_IO_SETUP,
"io_destroy": SYS_IO_DESTROY,
"io_submit": SYS_IO_SUBMIT,
"io_cancel": SYS_IO_CANCEL,
"io_getevents": SYS_IO_GETEVENTS,
"setxattr": SYS_SETXATTR,
"lsetxattr": SYS_LSETXATTR,
"fsetxattr": SYS_FSETXATTR,
"getxattr": SYS_GETXATTR,
"lgetxattr": SYS_LGETXATTR,
"fgetxattr": SYS_FGETXATTR,
"listxattr": SYS_LISTXATTR,
"llistxattr": SYS_LLISTXATTR,
"flistxattr": SYS_FLISTXATTR,
"removexattr": SYS_REMOVEXATTR,
"lremovexattr": SYS_LREMOVEXATTR,
"fremovexattr": SYS_FREMOVEXATTR,
"getcwd": SYS_GETCWD,
"lookup_dcookie": SYS_LOOKUP_DCOOKIE,
"eventfd2": SYS_EVENTFD2,
"epoll_create1": SYS_EPOLL_CREATE1,
"epoll_ctl": SYS_EPOLL_CTL,
"epoll_pwait": SYS_EPOLL_PWAIT,
"dup": SYS_DUP,
"dup3": SYS_DUP3,
"fcntl": SYS_FCNTL,
"inotify_init1": SYS_INOTIFY_INIT1,
"inotify_add_watch": SYS_INOTIFY_ADD_WATCH,
"inotify_rm_watch": SYS_INOTIFY_RM_WATCH,
"ioctl": SYS_IOCTL,
"ioprio_set": SYS_IOPRIO_SET,
"ioprio_get": SYS_IOPRIO_GET,
"flock": SYS_FLOCK,
"mknodat": SYS_MKNODAT,
"mkdirat": SYS_MKDIRAT,
"unlinkat": SYS_UNLINKAT,
"symlinkat": SYS_SYMLINKAT,
"linkat": SYS_LINKAT,
"renameat": SYS_RENAMEAT,
"umount2": SYS_UMOUNT2,
"mount": SYS_MOUNT,
"pivot_root": SYS_PIVOT_ROOT,
"nfsservctl": SYS_NFSSERVCTL,
"statfs": SYS_STATFS,
"fstatfs": SYS_FSTATFS,
"truncate": SYS_TRUNCATE,
"ftruncate": SYS_FTRUNCATE,
"fallocate": SYS_FALLOCATE,
"faccessat": SYS_FACCESSAT,
"chdir": SYS_CHDIR,
"fchdir": SYS_FCHDIR,
"chroot": SYS_CHROOT,
"fchmod": SYS_FCHMOD,
"fchmodat": SYS_FCHMODAT,
"fchownat": SYS_FCHOWNAT,
"fchown": SYS_FCHOWN,
"openat": SYS_OPENAT,
"close": SYS_CLOSE,
"vhangup": SYS_VHANGUP,
"pipe2": SYS_PIPE2,
"quotactl": SYS_QUOTACTL,
"getdents64": SYS_GETDENTS64,
"lseek": SYS_LSEEK,
"read": SYS_READ,
"write": SYS_WRITE,
"readv": SYS_READV,
"writev": SYS_WRITEV,
"pread64": SYS_PREAD64,
"pwrite64": SYS_PWRITE64,
"preadv": SYS_PREADV,
"pwritev": SYS_PWRITEV,
"sendfile": SYS_SENDFILE,
"pselect6": SYS_PSELECT6,
"ppoll": SYS_PPOLL,
"signalfd4": SYS_SIGNALFD4,
"vmsplice": SYS_VMSPLICE,
"splice": SYS_SPLICE,
"tee": SYS_TEE,
"readlinkat": SYS_READLINKAT,
"newfstatat": SYS_NEWFSTATAT,
"fstat": SYS_FSTAT,
"sync": SYS_SYNC,
"fsync": SYS_FSYNC,
"fdatasync": SYS_FDATASYNC,
"sync_file_range": SYS_SYNC_FILE_RANGE,
"timerfd_create": SYS_TIMERFD_CREATE,
"timerfd_settime": SYS_TIMERFD_SETTIME,
"timerfd_gettime": SYS_TIMERFD_GETTIME,
"utimensat": SYS_UTIMENSAT,
"acct": SYS_ACCT,
"capget": SYS_CAPGET,
"capset": SYS_CAPSET,
"personality": SYS_PERSONALITY,
"exit": SYS_EXIT,
"exit_group": SYS_EXIT_GROUP,
"waitid": SYS_WAITID,
"set_tid_address": SYS_SET_TID_ADDRESS,
"unshare": SYS_UNSHARE,
"futex": SYS_FUTEX,
"set_robust_list": SYS_SET_ROBUST_LIST,
"get_robust_list": SYS_GET_ROBUST_LIST,
"nanosleep": SYS_NANOSLEEP,
"getitimer": SYS_GETITIMER,
"setitimer": SYS_SETITIMER,
"kexec_load": SYS_KEXEC_LOAD,
"init_module": SYS_INIT_MODULE,
"delete_module": SYS_DELETE_MODULE,
"timer_create": SYS_TIMER_CREATE,
"timer_gettime": SYS_TIMER_GETTIME,
"timer_getoverrun": SYS_TIMER_GETOVERRUN,
"timer_settime": SYS_TIMER_SETTIME,
"timer_delete": SYS_TIMER_DELETE,
"clock_settime": SYS_CLOCK_SETTIME,
"clock_gettime": SYS_CLOCK_GETTIME,
"clock_getres": SYS_CLOCK_GETRES,
"clock_nanosleep": SYS_CLOCK_NANOSLEEP,
"syslog": SYS_SYSLOG,
"ptrace": SYS_PTRACE,
"sched_setparam": SYS_SCHED_SETPARAM,
"sched_setscheduler": SYS_SCHED_SETSCHEDULER,
"sched_getscheduler": SYS_SCHED_GETSCHEDULER,
"sched_getparam": SYS_SCHED_GETPARAM,
"sched_setaffinity": SYS_SCHED_SETAFFINITY,
"sched_getaffinity": SYS_SCHED_GETAFFINITY,
"sched_yield": SYS_SCHED_YIELD,
"sched_get_priority_max": SYS_SCHED_GET_PRIORITY_MAX,
"sched_get_priority_min": SYS_SCHED_GET_PRIORITY_MIN,
"sched_rr_get_interval": SYS_SCHED_RR_GET_INTERVAL,
"restart_syscall": SYS_RESTART_SYSCALL,
"kill": SYS_KILL,
"tkill": SYS_TKILL,
"tgkill": SYS_TGKILL,
"sigaltstack": SYS_SIGALTSTACK,
"rt_sigsuspend": SYS_RT_SIGSUSPEND,
"rt_sigaction": SYS_RT_SIGACTION,
"rt_sigprocmask": SYS_RT_SIGPROCMASK,
"rt_sigpending": SYS_RT_SIGPENDING,
"rt_sigtimedwait": SYS_RT_SIGTIMEDWAIT,
"rt_sigqueueinfo": SYS_RT_SIGQUEUEINFO,
"rt_sigreturn": SYS_RT_SIGRETURN,
"setpriority": SYS_SETPRIORITY,
"getpriority": SYS_GETPRIORITY,
"reboot": SYS_REBOOT,
"setregid": SYS_SETREGID,
"setgid": SYS_SETGID,
"setreuid": SYS_SETREUID,
"setuid": SYS_SETUID,
"setresuid": SYS_SETRESUID,
"getresuid": SYS_GETRESUID,
"setresgid": SYS_SETRESGID,
"getresgid": SYS_GETRESGID,
"setfsuid": SYS_SETFSUID,
"setfsgid": SYS_SETFSGID,
"times": SYS_TIMES,
"setpgid": SYS_SETPGID,
"getpgid": SYS_GETPGID,
"getsid": SYS_GETSID,
"setsid": SYS_SETSID,
"getgroups": SYS_GETGROUPS,
"setgroups": SYS_SETGROUPS,
"uname": SYS_UNAME,
"sethostname": SYS_SETHOSTNAME,
"setdomainname": SYS_SETDOMAINNAME,
"getrlimit": SYS_GETRLIMIT,
"setrlimit": SYS_SETRLIMIT,
"getrusage": SYS_GETRUSAGE,
"umask": SYS_UMASK,
"prctl": SYS_PRCTL,
"getcpu": SYS_GETCPU,
"gettimeofday": SYS_GETTIMEOFDAY,
"settimeofday": SYS_SETTIMEOFDAY,
"adjtimex": SYS_ADJTIMEX,
"getpid": SYS_GETPID,
"getppid": SYS_GETPPID,
"getuid": SYS_GETUID,
"geteuid": SYS_GETEUID,
"getgid": SYS_GETGID,
"getegid": SYS_GETEGID,
"gettid": SYS_GETTID,
"sysinfo": SYS_SYSINFO,
"mq_open": SYS_MQ_OPEN,
"mq_unlink": SYS_MQ_UNLINK,
"mq_timedsend": SYS_MQ_TIMEDSEND,
"mq_timedreceive": SYS_MQ_TIMEDRECEIVE,
"mq_notify": SYS_MQ_NOTIFY,
"mq_getsetattr": SYS_MQ_GETSETATTR,
"msgget": SYS_MSGGET,
"msgctl": SYS_MSGCTL,
"msgrcv": SYS_MSGRCV,
"msgsnd": SYS_MSGSND,
"semget": SYS_SEMGET,
"semctl": SYS_SEMCTL,
"semtimedop": SYS_SEMTIMEDOP,
"semop": SYS_SEMOP,
"shmget": SYS_SHMGET,
"shmctl": SYS_SHMCTL,
"shmat": SYS_SHMAT,
"shmdt": SYS_SHMDT,
"socket": SYS_SOCKET,
"socketpair": SYS_SOCKETPAIR,
"bind": SYS_BIND,
"listen": SYS_LISTEN,
"accept": SYS_ACCEPT,
"connect": SYS_CONNECT,
"getsockname": SYS_GETSOCKNAME,
"getpeername": SYS_GETPEERNAME,
"sendto": SYS_SENDTO,
"recvfrom": SYS_RECVFROM,
"setsockopt": SYS_SETSOCKOPT,
"getsockopt": SYS_GETSOCKOPT,
"shutdown": SYS_SHUTDOWN,
"sendmsg": SYS_SENDMSG,
"recvmsg": SYS_RECVMSG,
"readahead": SYS_READAHEAD,
"brk": SYS_BRK,
"munmap": SYS_MUNMAP,
"mremap": SYS_MREMAP,
"add_key": SYS_ADD_KEY,
"request_key": SYS_REQUEST_KEY,
"keyctl": SYS_KEYCTL,
"clone": SYS_CLONE,
"execve": SYS_EXECVE,
"mmap": SYS_MMAP,
"fadvise64": SYS_FADVISE64,
"swapon": SYS_SWAPON,
"swapoff": SYS_SWAPOFF,
"mprotect": SYS_MPROTECT,
"msync": SYS_MSYNC,
"mlock": SYS_MLOCK,
"munlock": SYS_MUNLOCK,
"mlockall": SYS_MLOCKALL,
"munlockall": SYS_MUNLOCKALL,
"mincore": SYS_MINCORE,
"madvise": SYS_MADVISE,
"remap_file_pages": SYS_REMAP_FILE_PAGES,
"mbind": SYS_MBIND,
"get_mempolicy": SYS_GET_MEMPOLICY,
"set_mempolicy": SYS_SET_MEMPOLICY,
"migrate_pages": SYS_MIGRATE_PAGES,
"move_pages": SYS_MOVE_PAGES,
"rt_tgsigqueueinfo": SYS_RT_TGSIGQUEUEINFO,
"perf_event_open": SYS_PERF_EVENT_OPEN,
"accept4": SYS_ACCEPT4,
"recvmmsg": SYS_RECVMMSG,
"wait4": SYS_WAIT4,
"prlimit64": SYS_PRLIMIT64,
"fanotify_init": SYS_FANOTIFY_INIT,
"fanotify_mark": SYS_FANOTIFY_MARK,
"name_to_handle_at": SYS_NAME_TO_HANDLE_AT,
"open_by_handle_at": SYS_OPEN_BY_HANDLE_AT,
"clock_adjtime": SYS_CLOCK_ADJTIME,
"syncfs": SYS_SYNCFS,
"setns": SYS_SETNS,
"sendmmsg": SYS_SENDMMSG,
"process_vm_readv": SYS_PROCESS_VM_READV,
"process_vm_writev": SYS_PROCESS_VM_WRITEV,
"kcmp": SYS_KCMP,
"finit_module": SYS_FINIT_MODULE,
"sched_setattr": SYS_SCHED_SETATTR,
"sched_getattr": SYS_SCHED_GETATTR,
"renameat2": SYS_RENAMEAT2,
"seccomp": SYS_SECCOMP,
"getrandom": SYS_GETRANDOM,
"memfd_create": SYS_MEMFD_CREATE,
"bpf": SYS_BPF,
"execveat": SYS_EXECVEAT,
"userfaultfd": SYS_USERFAULTFD,
"membarrier": SYS_MEMBARRIER,
"mlock2": SYS_MLOCK2,
"copy_file_range": SYS_COPY_FILE_RANGE,
"preadv2": SYS_PREADV2,
"pwritev2": SYS_PWRITEV2,
"pkey_mprotect": SYS_PKEY_MPROTECT,
"pkey_alloc": SYS_PKEY_ALLOC,
"pkey_free": SYS_PKEY_FREE,
"statx": SYS_STATX,
"io_pgetevents": SYS_IO_PGETEVENTS,
"rseq": SYS_RSEQ,
"kexec_file_load": SYS_KEXEC_FILE_LOAD,
"pidfd_send_signal": SYS_PIDFD_SEND_SIGNAL,
"io_uring_setup": SYS_IO_URING_SETUP,
"io_uring_enter": SYS_IO_URING_ENTER,
"io_uring_register": SYS_IO_URING_REGISTER,
"open_tree": SYS_OPEN_TREE,
"move_mount": SYS_MOVE_MOUNT,
"fsopen": SYS_FSOPEN,
"fsconfig": SYS_FSCONFIG,
"fsmount": SYS_FSMOUNT,
"fspick": SYS_FSPICK,
"pidfd_open": SYS_PIDFD_OPEN,
"clone3": SYS_CLONE3,
"close_range": SYS_CLOSE_RANGE,
"openat2": SYS_OPENAT2,
"pidfd_getfd": SYS_PIDFD_GETFD,
"faccessat2": SYS_FACCESSAT2,
"process_madvise": SYS_PROCESS_MADVISE,
"epoll_pwait2": SYS_EPOLL_PWAIT2,
"mount_setattr": SYS_MOUNT_SETATTR,
"quotactl_fd": SYS_QUOTACTL_FD,
"landlock_create_ruleset": SYS_LANDLOCK_CREATE_RULESET,
"landlock_add_rule": SYS_LANDLOCK_ADD_RULE,
"landlock_restrict_self": SYS_LANDLOCK_RESTRICT_SELF,
"memfd_secret": SYS_MEMFD_SECRET,
"process_mrelease": SYS_PROCESS_MRELEASE,
"futex_waitv": SYS_FUTEX_WAITV,
"set_mempolicy_home_node": SYS_SET_MEMPOLICY_HOME_NODE,
"cachestat": SYS_CACHESTAT,
"fchmodat2": SYS_FCHMODAT2,
"map_shadow_stack": SYS_MAP_SHADOW_STACK,
"futex_wake": SYS_FUTEX_WAKE,
"futex_wait": SYS_FUTEX_WAIT,
"futex_requeue": SYS_FUTEX_REQUEUE,
"statmount": SYS_STATMOUNT,
"listmount": SYS_LISTMOUNT,
"lsm_get_self_attr": SYS_LSM_GET_SELF_ATTR,
"lsm_set_self_attr": SYS_LSM_SET_SELF_ATTR,
"lsm_list_modules": SYS_LSM_LIST_MODULES,
"mseal": SYS_MSEAL,
}
const (
SYS_USERFAULTFD = 282
SYS_MEMBARRIER = 283
SYS_MLOCK2 = 284
SYS_COPY_FILE_RANGE = 285
SYS_PREADV2 = 286
SYS_PWRITEV2 = 287
SYS_PKEY_MPROTECT = 288
SYS_PKEY_ALLOC = 289
SYS_PKEY_FREE = 290
SYS_STATX = 291
SYS_IO_PGETEVENTS = 292
SYS_RSEQ = 293
SYS_KEXEC_FILE_LOAD = 294
SYS_PIDFD_SEND_SIGNAL = 424
SYS_IO_URING_SETUP = 425
SYS_IO_URING_ENTER = 426
SYS_IO_URING_REGISTER = 427
SYS_OPEN_TREE = 428
SYS_MOVE_MOUNT = 429
SYS_FSOPEN = 430
SYS_FSCONFIG = 431
SYS_FSMOUNT = 432
SYS_FSPICK = 433
SYS_PIDFD_OPEN = 434
SYS_CLONE3 = 435
SYS_CLOSE_RANGE = 436
SYS_OPENAT2 = 437
SYS_PIDFD_GETFD = 438
SYS_FACCESSAT2 = 439
SYS_PROCESS_MADVISE = 440
SYS_EPOLL_PWAIT2 = 441
SYS_MOUNT_SETATTR = 442
SYS_QUOTACTL_FD = 443
SYS_LANDLOCK_CREATE_RULESET = 444
SYS_LANDLOCK_ADD_RULE = 445
SYS_LANDLOCK_RESTRICT_SELF = 446
SYS_MEMFD_SECRET = 447
SYS_PROCESS_MRELEASE = 448
SYS_FUTEX_WAITV = 449
SYS_SET_MEMPOLICY_HOME_NODE = 450
SYS_CACHESTAT = 451
SYS_FCHMODAT2 = 452
SYS_MAP_SHADOW_STACK = 453
SYS_FUTEX_WAKE = 454
SYS_FUTEX_WAIT = 455
SYS_FUTEX_REQUEUE = 456
SYS_STATMOUNT = 457
SYS_LISTMOUNT = 458
SYS_LSM_GET_SELF_ATTR = 459
SYS_LSM_SET_SELF_ATTR = 460
SYS_LSM_LIST_MODULES = 461
SYS_MSEAL = 462
)

View File

@ -2,6 +2,16 @@ package container
import ( import (
"syscall" "syscall"
"unsafe"
)
const (
O_PATH = 0x200000
PR_SET_NO_NEW_PRIVS = 0x26
CAP_SYS_ADMIN = 0x15
CAP_SETPCAP = 0x8
) )
const ( const (
@ -18,6 +28,42 @@ func SetDumpable(dumpable uintptr) error {
return nil return nil
} }
const (
_LINUX_CAPABILITY_VERSION_3 = 0x20080522
PR_CAP_AMBIENT = 0x2f
PR_CAP_AMBIENT_RAISE = 0x2
PR_CAP_AMBIENT_CLEAR_ALL = 0x4
)
type (
capHeader struct {
version uint32
pid int32
}
capData struct {
effective uint32
permitted uint32
inheritable uint32
}
)
// See CAP_TO_INDEX in linux/capability.h:
func capToIndex(cap uintptr) uintptr { return cap >> 5 }
// See CAP_TO_MASK in linux/capability.h:
func capToMask(cap uintptr) uint32 { return 1 << uint(cap&31) }
func capset(hdrp *capHeader, datap *[2]capData) error {
if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET,
uintptr(unsafe.Pointer(hdrp)),
uintptr(unsafe.Pointer(&datap[0])), 0); errno != 0 {
return errno
}
return nil
}
// IgnoringEINTR makes a function call and repeats it if it returns an // IgnoringEINTR makes a function call and repeats it if it returns an
// EINTR error. This appears to be required even though we install all // EINTR error. This appears to be required even though we install all
// signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846. // signal handlers with SA_RESTART: see #22838, #38033, #38836, #40846.

View File

@ -1,7 +0,0 @@
package container
const (
O_PATH = 0x200000
PR_SET_NO_NEW_PRIVS = 0x26
)

View File

@ -185,23 +185,17 @@
''; '';
}; };
generateSyscallTable = generateSyscallTable = pkgs.mkShell {
let # this should be made cross-platform via nix
GOARCH = { shellHook = "exec ${pkgs.writeShellScript "generate-syscall-table" ''
x86_64-linux = "amd64"; set -e
aarch64-linux = "arm64"; ${pkgs.perl}/bin/perl \
}; sandbox/seccomp/mksysnum_linux.pl \
in ${pkgs.linuxHeaders}/include/asm/unistd_64.h | \
pkgs.mkShell { ${pkgs.go}/bin/gofmt > \
shellHook = "exec ${pkgs.writeShellScript "generate-syscall-table" '' sandbox/seccomp/syscall_linux_amd64.go
set -e ''}";
${pkgs.perl}/bin/perl \ };
container/seccomp/mksysnum_linux.pl \
${pkgs.linuxHeaders}/include/asm/unistd_64.h | \
${pkgs.go}/bin/gofmt > \
container/seccomp/syscall_linux_${GOARCH.${system}}.go
''}";
};
} }
); );
}; };

View File

@ -1,5 +1,3 @@
//go:build testtool
/* /*
Package sandbox provides utilities for checking sandbox outcome. Package sandbox provides utilities for checking sandbox outcome.
@ -17,6 +15,7 @@ import (
"log" "log"
"os" "os"
"syscall" "syscall"
"time"
) )
var ( var (
@ -41,10 +40,13 @@ type T struct {
MountsPath string MountsPath string
} }
func (t *T) MustCheckFile(wantFilePath string) { func (t *T) MustCheckFile(wantFilePath, markerPath string) {
var want *TestCase var want *TestCase
mustDecode(wantFilePath, &want) mustDecode(wantFilePath, &want)
t.MustCheck(want) t.MustCheck(want)
if _, err := os.Create(markerPath); err != nil {
fatalf("cannot create success marker: %v", err)
}
} }
func (t *T) MustCheck(want *TestCase) { func (t *T) MustCheck(want *TestCase) {
@ -163,10 +165,31 @@ func CheckFilter(pid int, want string) error {
}() }()
h := sha512.New() h := sha512.New()
{
getFilter:
buf, err := getFilter[[8]byte](pid, 0)
/* this is not how ESRCH should be handled: the manpage advises the
use of waitpid, however that is not applicable for attaching to an
arbitrary process, and spawning target process here is not easily
possible under the current testing framework;
despite checking for /proc/pid/status indicating state t (tracing stop),
it does not appear to be directly related to the internal state used to
determine whether a process is ready to accept ptrace operations, it also
introduces a TOCTOU that is irrelevant in the testing vm; this behaviour
is kept anyway as it reduces the average iterations required here;
since this code is only ever compiled into the test program, whatever
implications this ugliness might have should not hurt anyone */
if errors.Is(err, syscall.ESRCH) {
time.Sleep(100 * time.Millisecond)
goto getFilter
}
if err != nil {
return err
}
if buf, err := getFilter[[8]byte](pid, 0); err != nil {
return err
} else {
for _, b := range buf { for _, b := range buf {
h.Write(b[:]) h.Write(b[:])
} }

View File

@ -1,5 +1,3 @@
//go:build testtool
package sandbox package sandbox
import ( import (

View File

@ -1,4 +1,4 @@
system: lib: testProgram: lib: testProgram:
let let
fs = mode: dir: data: { fs = mode: dir: data: {
mode = lib.fromHexString mode; mode = lib.fromHexString mode;
@ -31,7 +31,6 @@ let
fs fs
ent ent
ignore ignore
system
; ;
}; };
in in
@ -44,17 +43,13 @@ let
device device
mapRealUid mapRealUid
useCommonPaths useCommonPaths
userns
; ;
share = testProgram; share = testProgram;
packages = [ ]; packages = [ ];
path = "${testProgram}/bin/hakurei-test"; path = "${testProgram}/bin/hakurei-test";
args = [ args = [
"test" "test"
"-t"
(toString (builtins.toFile "hakurei-${tc.name}-want.json" (builtins.toJSON tc.want))) (toString (builtins.toFile "hakurei-${tc.name}-want.json" (builtins.toJSON tc.want)))
"-s"
tc.expectedFilter.${system}
]; ];
}; };
@ -65,5 +60,4 @@ in
${testCaseName "tty"} = callTestCase ./tty.nix 2; ${testCaseName "tty"} = callTestCase ./tty.nix 2;
${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3; ${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3;
${testCaseName "device"} = callTestCase ./device.nix 4; ${testCaseName "device"} = callTestCase ./device.nix 4;
${testCaseName "pdlike"} = callTestCase ./pdlike.nix 5;
} }

View File

@ -2,35 +2,13 @@
fs, fs,
ent, ent,
ignore, ignore,
system,
}: }:
let
extraPaths = {
x86_64-linux = {
fd = "fd0";
sr = {
sr0 = fs "80001ff" null null;
};
};
aarch64-linux = {
fd = "mtdblock0";
sr = { };
};
};
in
{ {
name = "device"; name = "device";
tty = false; tty = false;
device = true; device = true;
mapRealUid = false; mapRealUid = false;
useCommonPaths = true; useCommonPaths = true;
userns = false;
# 0, PresetStrict
expectedFilter = {
x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735";
aarch64-linux = "79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297";
};
want = { want = {
env = [ env = [
@ -135,21 +113,19 @@ in
} null; } null;
} null; } null;
sys = fs "800001c0" { sys = fs "800001c0" {
block = fs "800001ed" ( block = fs "800001ed" {
{ fd0 = fs "80001ff" null null;
${extraPaths.${system}.fd} = fs "80001ff" null null; loop0 = fs "80001ff" null null;
loop0 = fs "80001ff" null null; loop1 = fs "80001ff" null null;
loop1 = fs "80001ff" null null; loop2 = fs "80001ff" null null;
loop2 = fs "80001ff" null null; loop3 = fs "80001ff" null null;
loop3 = fs "80001ff" null null; loop4 = fs "80001ff" null null;
loop4 = fs "80001ff" null null; loop5 = fs "80001ff" null null;
loop5 = fs "80001ff" null null; loop6 = fs "80001ff" null null;
loop6 = fs "80001ff" null null; loop7 = fs "80001ff" null null;
loop7 = fs "80001ff" null null; sr0 = fs "80001ff" null null;
vda = fs "80001ff" null null; vda = fs "80001ff" null null;
} } null;
// extraPaths.${system}.sr
) null;
bus = fs "800001ed" null null; bus = fs "800001ed" null null;
class = fs "800001ed" null null; class = fs "800001ed" null null;
dev = fs "800001ed" { dev = fs "800001ed" {

View File

@ -2,44 +2,13 @@
fs, fs,
ent, ent,
ignore, ignore,
system,
}: }:
let
extraPaths = {
x86_64-linux = {
fd = "fd0";
"/dev/dri" = {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
};
sr = {
sr0 = fs "80001ff" null null;
};
};
aarch64-linux = {
fd = "mtdblock0";
"/dev/dri" = null;
sr = { };
};
};
in
{ {
name = "mapuid"; name = "mapuid";
tty = false; tty = false;
device = false; device = false;
mapRealUid = true; mapRealUid = true;
useCommonPaths = true; useCommonPaths = true;
userns = false;
# 0, PresetStrict
expectedFilter = {
x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735";
aarch64-linux = "79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297";
};
want = { want = {
env = [ env = [
@ -60,7 +29,14 @@ in
bin = fs "800001ed" { sh = fs "80001ff" null null; } null; bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" { dev = fs "800001ed" {
core = fs "80001ff" null null; core = fs "80001ff" null null;
dri = fs "800001ed" extraPaths.${system}."/dev/dri" null; dri = fs "800001ed" {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
} null;
fd = fs "80001ff" null null; fd = fs "80001ff" null null;
full = fs "42001b6" null null; full = fs "42001b6" null null;
mqueue = fs "801001ff" { } null; mqueue = fs "801001ff" { } null;
@ -161,21 +137,19 @@ in
} null; } null;
} null; } null;
sys = fs "800001c0" { sys = fs "800001c0" {
block = fs "800001ed" ( block = fs "800001ed" {
{ fd0 = fs "80001ff" null null;
${extraPaths.${system}.fd} = fs "80001ff" null null; loop0 = fs "80001ff" null null;
loop0 = fs "80001ff" null null; loop1 = fs "80001ff" null null;
loop1 = fs "80001ff" null null; loop2 = fs "80001ff" null null;
loop2 = fs "80001ff" null null; loop3 = fs "80001ff" null null;
loop3 = fs "80001ff" null null; loop4 = fs "80001ff" null null;
loop4 = fs "80001ff" null null; loop5 = fs "80001ff" null null;
loop5 = fs "80001ff" null null; loop6 = fs "80001ff" null null;
loop6 = fs "80001ff" null null; loop7 = fs "80001ff" null null;
loop7 = fs "80001ff" null null; sr0 = fs "80001ff" null null;
vda = fs "80001ff" null null; vda = fs "80001ff" null null;
} } null;
// extraPaths.${system}.sr
) null;
bus = fs "800001ed" null null; bus = fs "800001ed" null null;
class = fs "800001ed" null null; class = fs "800001ed" null null;
dev = fs "800001ed" { dev = fs "800001ed" {

View File

@ -1,274 +0,0 @@
{
fs,
ent,
ignore,
system,
}:
let
extraPaths = {
x86_64-linux = {
fd = "fd0";
"/dev/dri" = {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
};
sr = {
sr0 = fs "80001ff" null null;
};
};
aarch64-linux = {
fd = "mtdblock0";
"/dev/dri" = null;
sr = { };
};
};
in
{
name = "pdlike";
tty = true;
device = false;
mapRealUid = false;
useCommonPaths = false;
userns = true;
# 0, PresetExt | PresetDenyDevel
expectedFilter = {
x86_64-linux = "c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a";
aarch64-linux = "433ce9b911282d6dcc8029319fb79b816b60d5a795ec8fc94344dd027614d68f023166a91bb881faaeeedd26e3d89474e141e5a69a97e93b8984ca8f14999980";
};
want = {
env = [
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
"HOME=/var/lib/hakurei/u0/a5"
"PULSE_SERVER=unix:/run/user/65534/pulse/native"
"SHELL=/run/current-system/sw/bin/bash"
"TERM=linux"
"USER=u0_a5"
"WAYLAND_DISPLAY=wayland-0"
"XDG_RUNTIME_DIR=/run/user/65534"
"XDG_SESSION_CLASS=user"
"XDG_SESSION_TYPE=tty"
];
fs = fs "dead" {
".hakurei" = fs "800001ed" { } null;
bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" {
console = fs "4200190" null null;
core = fs "80001ff" null null;
dri = fs "800001ed" extraPaths.${system}."/dev/dri" null;
fd = fs "80001ff" null null;
full = fs "42001b6" null null;
mqueue = fs "801001ff" { } null;
null = fs "42001b6" null "";
ptmx = fs "80001ff" null null;
pts = fs "800001ed" { ptmx = fs "42001b6" null null; } null;
random = fs "42001b6" null null;
shm = fs "800001ed" { } null;
stderr = fs "80001ff" null null;
stdin = fs "80001ff" null null;
stdout = fs "80001ff" null null;
tty = fs "42001b6" null null;
urandom = fs "42001b6" null null;
zero = fs "42001b6" null null;
} null;
etc = fs "800001ed" {
".clean" = fs "80001ff" null null;
".host" = fs "800001c0" null null;
".updated" = fs "80001ff" null null;
"NIXOS" = fs "80001ff" null null;
"X11" = fs "80001ff" null null;
"alsa" = fs "80001ff" null null;
"bash_logout" = fs "80001ff" null null;
"bashrc" = fs "80001ff" null null;
"binfmt.d" = fs "80001ff" null null;
"dbus-1" = fs "80001ff" null null;
"default" = fs "80001ff" null null;
"dhcpcd.exit-hook" = fs "80001ff" null null;
"fonts" = fs "80001ff" null null;
"fstab" = fs "80001ff" null null;
"hsurc" = fs "80001ff" null null;
"fuse.conf" = fs "80001ff" null null;
"group" = fs "180" null "hakurei:x:65534:\n";
"host.conf" = fs "80001ff" null null;
"hostname" = fs "80001ff" null null;
"hosts" = fs "80001ff" null null;
"inputrc" = fs "80001ff" null null;
"issue" = fs "80001ff" null null;
"kbd" = fs "80001ff" null null;
"locale.conf" = fs "80001ff" null null;
"login.defs" = fs "80001ff" null null;
"lsb-release" = fs "80001ff" null null;
"lvm" = fs "80001ff" null null;
"machine-id" = fs "80001ff" null null;
"man_db.conf" = fs "80001ff" null null;
"modprobe.d" = fs "80001ff" null null;
"modules-load.d" = fs "80001ff" null null;
"mtab" = fs "80001ff" null null;
"nanorc" = fs "80001ff" null null;
"netgroup" = fs "80001ff" null null;
"nix" = fs "80001ff" null null;
"nixos" = fs "80001ff" null null;
"nscd.conf" = fs "80001ff" null null;
"nsswitch.conf" = fs "80001ff" null null;
"os-release" = fs "80001ff" null null;
"pam" = fs "80001ff" null null;
"pam.d" = fs "80001ff" null null;
"passwd" = fs "180" null "u0_a5:x:65534:65534:Hakurei:/var/lib/hakurei/u0/a5:/run/current-system/sw/bin/bash\n";
"pipewire" = fs "80001ff" null null;
"pki" = fs "80001ff" null null;
"polkit-1" = fs "80001ff" null null;
"profile" = fs "80001ff" null null;
"protocols" = fs "80001ff" null null;
"resolv.conf" = fs "80001ff" null null;
"resolvconf.conf" = fs "80001ff" null null;
"rpc" = fs "80001ff" null null;
"services" = fs "80001ff" null null;
"set-environment" = fs "80001ff" null null;
"shadow" = fs "80001ff" null null;
"shells" = fs "80001ff" null null;
"ssh" = fs "80001ff" null null;
"ssl" = fs "80001ff" null null;
"static" = fs "80001ff" null null;
"subgid" = fs "80001ff" null null;
"subuid" = fs "80001ff" null null;
"sudoers" = fs "80001ff" null null;
"sway" = fs "80001ff" null null;
"sysctl.d" = fs "80001ff" null null;
"systemd" = fs "80001ff" null null;
"terminfo" = fs "80001ff" null null;
"tmpfiles.d" = fs "80001ff" null null;
"udev" = fs "80001ff" null null;
"vconsole.conf" = fs "80001ff" null null;
"xdg" = fs "80001ff" null null;
"zoneinfo" = fs "80001ff" null null;
} null;
nix = fs "800001c0" { store = fs "801001fd" null null; } null;
proc = fs "8000016d" null null;
run = fs "800001ed" {
current-system = fs "80001ff" null null;
opengl-driver = fs "80001ff" null null;
user = fs "800001ed" {
"65534" = fs "800001f8" {
bus = fs "10001fd" null null;
pulse = fs "800001c0" { native = fs "10001b6" null null; } null;
wayland-0 = fs "1000038" null null;
} null;
} null;
} null;
sys = fs "800001c0" {
block = fs "800001ed" (
{
${extraPaths.${system}.fd} = fs "80001ff" null null;
loop0 = fs "80001ff" null null;
loop1 = fs "80001ff" null null;
loop2 = fs "80001ff" null null;
loop3 = fs "80001ff" null null;
loop4 = fs "80001ff" null null;
loop5 = fs "80001ff" null null;
loop6 = fs "80001ff" null null;
loop7 = fs "80001ff" null null;
vda = fs "80001ff" null null;
}
// extraPaths.${system}.sr
) null;
bus = fs "800001ed" null null;
class = fs "800001ed" null null;
dev = fs "800001ed" {
block = fs "800001ed" null null;
char = fs "800001ed" null null;
} null;
devices = fs "800001ed" null null;
} null;
tmp = fs "800001f8" { } null;
usr = fs "800001c0" { bin = fs "800001ed" { env = fs "80001ff" null null; } null; } null;
var = fs "800001c0" {
lib = fs "800001c0" {
hakurei = fs "800001c0" {
u0 = fs "800001c0" {
a5 = fs "800001c0" {
".cache" = fs "800001ed" { ".keep" = fs "80001ff" null ""; } null;
".config" = fs "800001ed" {
"environment.d" = fs "800001ed" { "10-home-manager.conf" = fs "80001ff" null null; } null;
systemd = fs "800001ed" {
user = fs "800001ed" { "tray.target" = fs "80001ff" null null; } null;
} null;
} null;
".local" = fs "800001ed" {
share = fs "800001ed" {
dbus-1 = fs "800001ed" {
services = fs "800001ed" {
"ca.desrt.dconf.service" = fs "80001ff" null null;
} null;
} null;
} null;
state = fs "800001ed" {
".keep" = fs "80001ff" null "";
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
nix = fs "800001ed" {
profiles = fs "800001ed" {
home-manager = fs "80001ff" null null;
home-manager-1-link = fs "80001ff" null null;
profile = fs "80001ff" null null;
profile-1-link = fs "80001ff" null null;
} null;
} null;
} null;
} null;
".nix-defexpr" = fs "800001ed" {
channels = fs "80001ff" null null;
channels_root = fs "80001ff" null null;
} null;
".nix-profile" = fs "80001ff" null null;
} null;
} null;
} null;
} null;
run = fs "800001ed" { nscd = fs "800001ed" { } null; } null;
} null;
} null;
mount = [
(ent "/sysroot" "/" "rw,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005")
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000005,gid=1000005")
(ent "/" "/dev" "rw,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=1000005,gid=1000005")
(ent "/null" "/dev/null" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/zero" "/dev/zero" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/full" "/dev/full" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/random" "/dev/random" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/urandom" "/dev/urandom" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/tty" "/dev/tty" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666")
(ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666")
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/class" "/sys/class" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=4k,mode=755,uid=1000005,gid=1000005")
(ent "/tmp/hakurei.1000/runtime/5" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/tmp/hakurei.1000/tmpdir/5" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005")
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=1000005,gid=1000005")
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
(ent "/" "/var/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,size=8k,mode=755,uid=1000005,gid=1000005")
];
seccomp = true;
};
}

View File

@ -2,44 +2,13 @@
fs, fs,
ent, ent,
ignore, ignore,
system,
}: }:
let
extraPaths = {
x86_64-linux = {
fd = "fd0";
"/dev/dri" = {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
};
sr = {
sr0 = fs "80001ff" null null;
};
};
aarch64-linux = {
fd = "mtdblock0";
"/dev/dri" = null;
sr = { };
};
};
in
{ {
name = "preset"; name = "preset";
tty = false; tty = false;
device = false; device = false;
mapRealUid = false; mapRealUid = false;
useCommonPaths = false; useCommonPaths = false;
userns = false;
# 0, PresetStrict
expectedFilter = {
x86_64-linux = "e880298df2bd6751d0040fc21bc0ed4c00f95dc0d7ba506c244d8b8cf6866dba8ef4a33296f287b66cccc1d78e97026597f84cc7dec1573e148960fbd35cd735";
aarch64-linux = "79318538a3dc851314b6bd96f10d5861acb2aa7e13cb8de0619d0f6a76709d67f01ef3fd67e195862b02f9711e5b769bc4d1eb4fc0dfc41a723c89c968a93297";
};
want = { want = {
env = [ env = [
@ -60,7 +29,14 @@ in
bin = fs "800001ed" { sh = fs "80001ff" null null; } null; bin = fs "800001ed" { sh = fs "80001ff" null null; } null;
dev = fs "800001ed" { dev = fs "800001ed" {
core = fs "80001ff" null null; core = fs "80001ff" null null;
dri = fs "800001ed" extraPaths.${system}."/dev/dri" null; dri = fs "800001ed" {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
} null;
fd = fs "80001ff" null null; fd = fs "80001ff" null null;
full = fs "42001b6" null null; full = fs "42001b6" null null;
mqueue = fs "801001ff" { } null; mqueue = fs "801001ff" { } null;
@ -161,21 +137,19 @@ in
} null; } null;
} null; } null;
sys = fs "800001c0" { sys = fs "800001c0" {
block = fs "800001ed" ( block = fs "800001ed" {
{ fd0 = fs "80001ff" null null;
${extraPaths.${system}.fd} = fs "80001ff" null null; loop0 = fs "80001ff" null null;
loop0 = fs "80001ff" null null; loop1 = fs "80001ff" null null;
loop1 = fs "80001ff" null null; loop2 = fs "80001ff" null null;
loop2 = fs "80001ff" null null; loop3 = fs "80001ff" null null;
loop3 = fs "80001ff" null null; loop4 = fs "80001ff" null null;
loop4 = fs "80001ff" null null; loop5 = fs "80001ff" null null;
loop5 = fs "80001ff" null null; loop6 = fs "80001ff" null null;
loop6 = fs "80001ff" null null; loop7 = fs "80001ff" null null;
loop7 = fs "80001ff" null null; sr0 = fs "80001ff" null null;
vda = fs "80001ff" null null; vda = fs "80001ff" null null;
} } null;
// extraPaths.${system}.sr
) null;
bus = fs "800001ed" null null; bus = fs "800001ed" null null;
class = fs "800001ed" null null; class = fs "800001ed" null null;
dev = fs "800001ed" { dev = fs "800001ed" {

View File

@ -2,44 +2,13 @@
fs, fs,
ent, ent,
ignore, ignore,
system,
}: }:
let
extraPaths = {
x86_64-linux = {
fd = "fd0";
"/dev/dri" = {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
};
sr = {
sr0 = fs "80001ff" null null;
};
};
aarch64-linux = {
fd = "mtdblock0";
"/dev/dri" = null;
sr = { };
};
};
in
{ {
name = "tty"; name = "tty";
tty = true; tty = true;
device = false; device = false;
mapRealUid = false; mapRealUid = false;
useCommonPaths = true; useCommonPaths = true;
userns = false;
# 0, PresetExt | PresetDenyNS | PresetDenyDevel
expectedFilter = {
x86_64-linux = "0b76007476c1c9e25dbf674c29fdf609a1656a70063e49327654e1b5360ad3da06e1a3e32bf80e961c5516ad83d4b9e7e9bde876a93797e27627d2555c25858b";
aarch64-linux = "cf1f4dc87436ba8ec95d268b663a6397bb0b4a5ac64d8557e6cc529d8b0f6f65dad3a92b62ed29d85eee9c6dde1267757a4d0f86032e8a45ca1bceadfa34cf5e";
};
want = { want = {
env = [ env = [
@ -61,7 +30,14 @@ in
dev = fs "800001ed" { dev = fs "800001ed" {
console = fs "4200190" null null; console = fs "4200190" null null;
core = fs "80001ff" null null; core = fs "80001ff" null null;
dri = fs "800001ed" extraPaths.${system}."/dev/dri" null; dri = fs "800001ed" {
by-path = fs "800001ed" {
"pci-0000:00:09.0-card" = fs "80001ff" null null;
"pci-0000:00:09.0-render" = fs "80001ff" null null;
} null;
card0 = fs "42001b0" null null;
renderD128 = fs "42001b6" null null;
} null;
fd = fs "80001ff" null null; fd = fs "80001ff" null null;
full = fs "42001b6" null null; full = fs "42001b6" null null;
mqueue = fs "801001ff" { } null; mqueue = fs "801001ff" { } null;
@ -162,21 +138,19 @@ in
} null; } null;
} null; } null;
sys = fs "800001c0" { sys = fs "800001c0" {
block = fs "800001ed" ( block = fs "800001ed" {
{ fd0 = fs "80001ff" null null;
${extraPaths.${system}.fd} = fs "80001ff" null null; loop0 = fs "80001ff" null null;
loop0 = fs "80001ff" null null; loop1 = fs "80001ff" null null;
loop1 = fs "80001ff" null null; loop2 = fs "80001ff" null null;
loop2 = fs "80001ff" null null; loop3 = fs "80001ff" null null;
loop3 = fs "80001ff" null null; loop4 = fs "80001ff" null null;
loop4 = fs "80001ff" null null; loop5 = fs "80001ff" null null;
loop5 = fs "80001ff" null null; loop6 = fs "80001ff" null null;
loop6 = fs "80001ff" null null; loop7 = fs "80001ff" null null;
loop7 = fs "80001ff" null null; sr0 = fs "80001ff" null null;
vda = fs "80001ff" null null; vda = fs "80001ff" null null;
} } null;
// extraPaths.${system}.sr
) null;
bus = fs "800001ed" null null; bus = fs "800001ed" null null;
class = fs "800001ed" null null; class = fs "800001ed" null null;
dev = fs "800001ed" { dev = fs "800001ed" {

View File

@ -75,6 +75,6 @@ in
} }
]; ];
apps = import ./case pkgs.system lib testProgram; apps = import ./case lib testProgram;
}; };
} }

View File

@ -1,5 +1,3 @@
//go:build testtool
package sandbox package sandbox
import ( import (

View File

@ -1,5 +1,3 @@
//go:build testtool
package sandbox_test package sandbox_test
import ( import (

View File

@ -1,5 +1,3 @@
//go:build testtool
package sandbox package sandbox
/* /*

View File

@ -1,5 +1,3 @@
//go:build testtool
package sandbox_test package sandbox_test
import ( import (

View File

@ -1,11 +1,13 @@
//go:build testtool
package sandbox package sandbox
import ( import (
"errors" "bufio"
"fmt" "fmt"
"io"
"os"
"strings"
"syscall" "syscall"
"time"
"unsafe" "unsafe"
) )
@ -37,19 +39,49 @@ func ptrace(op uintptr, pid, addr int, data unsafe.Pointer) (r uintptr, errno sy
} }
func ptraceAttach(pid int) error { func ptraceAttach(pid int) error {
const (
statePrefix = "State:"
stateSuffix = "t (tracing stop)"
)
var r io.ReadSeekCloser
if f, err := os.Open(fmt.Sprintf("/proc/%d/status", pid)); err != nil {
return err
} else {
r = f
}
if _, errno := ptrace(PTRACE_ATTACH, pid, 0, nil); errno != 0 { if _, errno := ptrace(PTRACE_ATTACH, pid, 0, nil); errno != 0 {
return &ptraceError{"PTRACE_ATTACH", errno} return &ptraceError{"PTRACE_ATTACH", errno}
} }
var status syscall.WaitStatus // ugly! but there does not appear to be another way
for { for {
if _, err := syscall.Wait4(pid, &status, syscall.WALL, nil); err != nil { time.Sleep(10 * time.Millisecond)
if errors.Is(err, syscall.EINTR) {
continue if _, err := r.Seek(0, io.SeekStart); err != nil {
} return err
fatalf("cannot waitpid: %v", err) }
s := bufio.NewScanner(r)
var found bool
for s.Scan() {
found = strings.HasPrefix(s.Text(), statePrefix)
if found {
break
}
}
if err := s.Err(); err != nil {
return err
}
if !found {
return syscall.EBADE
}
if strings.HasSuffix(s.Text(), stateSuffix) {
break
} }
break
} }
return nil return nil

View File

@ -1,5 +1,3 @@
//go:build testtool
package sandbox package sandbox
import ( import (
@ -20,6 +18,7 @@ func trySyscalls() error {
trap, a1, a2, a3, a4, a5, a6 uintptr trap, a1, a2, a3, a4, a5, a6 uintptr
}{ }{
{"syslog", syscall.EPERM, syscall.SYS_SYSLOG, 0, NULL, NULL, NULL, NULL, NULL}, {"syslog", syscall.EPERM, syscall.SYS_SYSLOG, 0, NULL, NULL, NULL, NULL, NULL},
{"uselib", syscall.EPERM, syscall.SYS_USELIB, 0, NULL, NULL, NULL, NULL, NULL},
{"acct", syscall.EPERM, syscall.SYS_ACCT, 0, NULL, NULL, NULL, NULL, NULL}, {"acct", syscall.EPERM, syscall.SYS_ACCT, 0, NULL, NULL, NULL, NULL, NULL},
{"quotactl", syscall.EPERM, syscall.SYS_QUOTACTL, C.Q_GETQUOTA, NULL, uintptr(os.Getuid()), NULL, NULL, NULL}, {"quotactl", syscall.EPERM, syscall.SYS_QUOTACTL, C.Q_GETQUOTA, NULL, uintptr(os.Getuid()), NULL, NULL, NULL},
{"add_key", syscall.EPERM, syscall.SYS_ADD_KEY, NULL, NULL, NULL, NULL, NULL, NULL}, {"add_key", syscall.EPERM, syscall.SYS_ADD_KEY, NULL, NULL, NULL, NULL, NULL, NULL},

View File

@ -25,12 +25,6 @@ def swaymsg(command: str = "", succeed=True, type="command"):
return parsed return parsed
def check_filter(check_offset, name, pname):
pid = int(machine.wait_until_succeeds(f"pgrep -U {1000000+check_offset} -x {pname}", timeout=15))
hash = machine.succeed(f"sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 WAYLAND_DISPLAY=wayland-1 check-sandbox-{name} hash 2>/dev/null")
print(machine.succeed(f"hakurei-test -s {hash} filter {pid}"))
start_all() start_all()
machine.wait_for_unit("multi-user.target") machine.wait_for_unit("multi-user.target")
@ -41,9 +35,11 @@ print(machine.succeed("sudo -u alice -i hakurei version"))
machine.wait_for_file("/run/user/1000/wayland-1") machine.wait_for_file("/run/user/1000/wayland-1")
machine.wait_for_file("/tmp/sway-ipc.sock") machine.wait_for_file("/tmp/sway-ipc.sock")
# Check pd seccomp outcome: # Check seccomp outcome:
swaymsg("exec hakurei run cat") swaymsg("exec hakurei run cat")
check_filter(0, "pdlike", "cat") pid = int(machine.wait_until_succeeds("pgrep -U 1000000 -x cat", timeout=5))
print(machine.succeed(f"hakurei-test filter {pid} c698b081ff957afe17a6d94374537d37f2a63f6f9dd75da7546542407a9e32476ebda3312ba7785d7f618542bcfaf27ca27dcc2dddba852069d28bcfe8cad39a &>/dev/stdout", timeout=5))
machine.succeed(f"kill -TERM {pid}")
# Verify capabilities/securebits in user namespace: # Verify capabilities/securebits in user namespace:
print(machine.succeed("sudo -u alice -i hakurei run capsh --print")) print(machine.succeed("sudo -u alice -i hakurei run capsh --print"))
@ -61,14 +57,12 @@ def check_sandbox(name):
check_offset += 1 check_offset += 1
swaymsg(f"exec script /dev/null -E always -qec check-sandbox-{name}") swaymsg(f"exec script /dev/null -E always -qec check-sandbox-{name}")
machine.wait_for_file(f"/tmp/hakurei.1000/tmpdir/{check_offset}/sandbox-ok", timeout=15) machine.wait_for_file(f"/tmp/hakurei.1000/tmpdir/{check_offset}/sandbox-ok", timeout=15)
check_filter(check_offset, name, "hakurei-test")
check_sandbox("preset") check_sandbox("preset")
check_sandbox("tty") check_sandbox("tty")
check_sandbox("mapuid") check_sandbox("mapuid")
check_sandbox("device") check_sandbox("device")
check_sandbox("pdlike")
# Exit Sway and verify process exit status 0: # Exit Sway and verify process exit status 0:
swaymsg("exit", succeed=False) swaymsg("exit", succeed=False)

View File

@ -1,71 +1,39 @@
//go:build testtool
package main package main
import ( import (
"flag"
"fmt"
"log" "log"
"os" "os"
"os/signal"
"strconv" "strconv"
"strings" "strings"
"syscall"
"hakurei.app/test/sandbox" "hakurei.app/test/sandbox"
) )
var (
flagTestCase string
flagBpfHash string
)
func init() {
flag.StringVar(&flagTestCase, "t", "", "Nix store path to test case file")
flag.StringVar(&flagBpfHash, "s", "", "String representation of expected bpf sha512 hash")
}
func main() { func main() {
log.SetFlags(0) log.SetFlags(0)
log.SetPrefix("test: ") log.SetPrefix("test: ")
flag.Parse()
args := flag.Args() if len(os.Args) < 2 {
if len(args) < 1 { log.Fatal("invalid argument")
s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGINT)
go func() { <-s; log.Println("exiting on signal (likely from verifier)"); os.Exit(0) }()
(&sandbox.T{FS: os.DirFS("/")}).MustCheckFile(flagTestCase)
if _, err := os.Create("/tmp/sandbox-ok"); err != nil {
log.Fatalf("cannot create success marker: %v", err)
}
log.Println("blocking for seccomp check")
select {}
return
} }
switch args[0] { switch os.Args[1] {
case "filter": case "filter":
if len(args) != 2 { if len(os.Args) != 4 {
log.Fatal("invalid argument") log.Fatal("invalid argument")
} }
if pid, err := strconv.Atoi(strings.TrimSpace(args[1])); err != nil { if pid, err := strconv.Atoi(strings.TrimSpace(os.Args[2])); err != nil {
log.Fatalf("%s", err) log.Fatalf("%s", err)
} else if pid < 1 { } else if pid < 1 {
log.Fatalf("%d out of range", pid) log.Fatalf("%d out of range", pid)
} else { } else {
sandbox.MustCheckFilter(pid, flagBpfHash) sandbox.MustCheckFilter(pid, os.Args[3])
if err = syscall.Kill(pid, syscall.SIGINT); err != nil { return
log.Fatalf("cannot signal check process: %v", err)
}
} }
case "hash": // this eases the pain of passing the hash to python
fmt.Print(flagBpfHash)
default: default:
log.Fatal("invalid argument") (&sandbox.T{FS: os.DirFS("/")}).MustCheckFile(os.Args[1], "/tmp/sandbox-ok")
return
} }
} }

View File

@ -17,8 +17,6 @@ buildGoModule rec {
}; };
vendorHash = null; vendorHash = null;
tags = [ "testtool" ];
buildInputs = [ util-linux ]; buildInputs = [ util-linux ];
nativeBuildInputs = [ pkg-config ]; nativeBuildInputs = [ pkg-config ];