18 Commits

Author SHA1 Message Date
c5aefe5e9d internal/app/shim: check behaviour
All checks were successful
Test / Create distribution (push) Successful in 25s
Test / Sandbox (push) Successful in 39s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Hakurei (race detector) (push) Successful in 43s
Test / Hakurei (push) Successful in 44s
Test / Hpkg (push) Successful in 41s
Test / Flake checks (push) Successful in 1m20s
This does not yet have full coverage. Test cases covering failsafe paths and error injection will be added eventually.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-23 06:07:41 +09:00
0f8ffee44d internal/app: test case for hst template
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m8s
Test / Hakurei (push) Successful in 3m5s
Test / Hpkg (push) Successful in 3m58s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hakurei (race detector) (push) Successful in 4m42s
Test / Flake checks (push) Successful in 1m21s
This helps with other areas of the test suite as they're all based on hst.Template. This also helps contributors understand the behaviour of internal/app as hst.Template covers almost every aspect of it.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-23 04:46:58 +09:00
1685a4d000 cmd/hsu: reduce excessive test range
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 1m40s
Test / Sandbox (race detector) (push) Successful in 2m25s
Test / Hakurei (push) Successful in 2m36s
Test / Hakurei (race detector) (push) Successful in 3m13s
Test / Hpkg (push) Successful in 3m33s
Test / Flake checks (push) Successful in 1m24s
This is quite a simple piece of code, this many test cases is excessive and wastes time in the integration vm.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-23 04:32:32 +09:00
6c338b433a internal/app: reduce test case indentation
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m13s
Test / Hakurei (push) Successful in 3m9s
Test / Sandbox (race detector) (push) Successful in 4m3s
Test / Hpkg (push) Successful in 4m4s
Test / Hakurei (race detector) (push) Successful in 4m44s
Test / Flake checks (push) Successful in 1m28s
This improves readability on narrower displays.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 07:40:32 +09:00
8accd3b219 internal/app/shim: use syscall dispatcher
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m14s
Test / Hakurei (push) Successful in 3m9s
Test / Sandbox (race detector) (push) Successful in 3m58s
Test / Hpkg (push) Successful in 4m5s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m28s
This enables instrumented testing of the shim.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 06:58:45 +09:00
c5f59c5488 container/syscall: export prctl wrapper
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m13s
Test / Hakurei (push) Successful in 3m3s
Test / Sandbox (race detector) (push) Successful in 3m58s
Test / Hpkg (push) Successful in 4m4s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m27s
This is useful as package "syscall" does not provide such a wrapper. This change also improves error handling to fully conform to the manpage.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 05:26:54 +09:00
fcd9becf9a cmd/hsu: run in locked thread
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 1m41s
Test / Sandbox (race detector) (push) Successful in 2m23s
Test / Hakurei (push) Successful in 2m35s
Test / Hakurei (race detector) (push) Successful in 3m14s
Test / Hpkg (push) Successful in 3m39s
Test / Flake checks (push) Successful in 1m27s
Goroutine scheduling is not helpful in the setuid wrapper, it is not particularly harmful but lock here anyway.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 05:09:08 +09:00
622f945c22 container/init: check msg in entrypoint
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m14s
Test / Hakurei (push) Successful in 3m10s
Test / Sandbox (race detector) (push) Successful in 3m59s
Test / Hpkg (push) Successful in 4m8s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m27s
This covers invalid call to Init.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-22 04:20:08 +09:00
e94acc424c container/comp: rename from bits
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m19s
Test / Hakurei (push) Successful in 3m9s
Test / Hpkg (push) Successful in 3m53s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hakurei (race detector) (push) Successful in 4m43s
Test / Flake checks (push) Successful in 1m23s
This package will also hold syscall lookup tables for seccomp.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 20:54:03 +09:00
b1a4d801be hst/container: flags string representation
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m9s
Test / Sandbox (race detector) (push) Successful in 3m56s
Test / Hpkg (push) Successful in 4m5s
Test / Hakurei (race detector) (push) Successful in 4m42s
Test / Hakurei (push) Successful in 2m9s
Test / Flake checks (push) Successful in 1m28s
This is useful for a user-facing representation other than JSON. This also gets rid of the ugly, outdated flags string builder in cmd/hakurei.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 20:29:52 +09:00
56beae17fe test: assert hst CGO_ENABLED=0
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 40s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Hpkg (push) Successful in 41s
Test / Hakurei (push) Successful in 2m29s
Test / Hakurei (race detector) (push) Successful in 3m7s
Test / Flake checks (push) Successful in 1m24s
The hst package only deals with data serialisation, however since many parts of hakurei make use of C libraries in some way it can be easy to inadvertently depend on cgo.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 19:49:04 +09:00
ea978101b1 cmd/hakurei/parse: close config fd
All checks were successful
Test / Create distribution (push) Successful in 32s
Test / Sandbox (push) Successful in 2m9s
Test / Hakurei (push) Successful in 3m5s
Test / Sandbox (race detector) (push) Successful in 3m54s
Test / Hpkg (push) Successful in 3m57s
Test / Hakurei (race detector) (push) Successful in 4m43s
Test / Flake checks (push) Successful in 1m20s
This is cleaner than relying on the finalizer.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 06:05:36 +09:00
fbd1638e7f test/interactive/trace: update nix attribute
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 40s
Test / Sandbox (race detector) (push) Successful in 40s
Test / Hakurei (race detector) (push) Successful in 44s
Test / Hakurei (push) Successful in 45s
Test / Hpkg (push) Successful in 42s
Test / Flake checks (push) Successful in 1m28s
Updated according to evaluation warning.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 06:03:09 +09:00
d42067df7c cmd/hakurei/json: friendly error messages
All checks were successful
Test / Create distribution (push) Successful in 25s
Test / Sandbox (push) Successful in 39s
Test / Sandbox (race detector) (push) Successful in 39s
Test / Hakurei (push) Successful in 44s
Test / Hakurei (race detector) (push) Successful in 43s
Test / Hpkg (push) Successful in 41s
Test / Flake checks (push) Successful in 1m23s
This change handles errors returned by encoding/json and prints significantly cleaner messages.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 05:17:25 +09:00
b9459a80c7 container/init: check use constants for open flags
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m11s
Test / Hakurei (push) Successful in 3m8s
Test / Sandbox (race detector) (push) Successful in 3m58s
Test / Hpkg (push) Successful in 4m6s
Test / Hakurei (race detector) (push) Successful in 4m45s
Test / Flake checks (push) Successful in 1m28s
These bits are arch-specific.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 03:13:58 +09:00
f8189d1488 container/syscall: dot-import syscall
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m12s
Test / Hakurei (push) Successful in 3m7s
Test / Hpkg (push) Successful in 3m57s
Test / Sandbox (race detector) (push) Successful in 4m2s
Test / Hakurei (race detector) (push) Successful in 4m44s
Test / Flake checks (push) Successful in 1m38s
This avoids having arch-specific constants for arm64.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 03:09:14 +09:00
5063b774c1 hst: expose version string
All checks were successful
Test / Create distribution (push) Successful in 36s
Test / Sandbox (push) Successful in 2m6s
Test / Hakurei (push) Successful in 3m0s
Test / Hpkg (push) Successful in 3m56s
Test / Sandbox (race detector) (push) Successful in 4m0s
Test / Hakurei (race detector) (push) Successful in 4m44s
Test / Flake checks (push) Successful in 1m20s
The hst API is tied to this version string.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 01:56:44 +09:00
766dd89ffa internal: clean up build strings
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m9s
Test / Hakurei (push) Successful in 3m5s
Test / Hpkg (push) Successful in 4m4s
Test / Sandbox (race detector) (push) Successful in 4m9s
Test / Hakurei (race detector) (push) Successful in 4m46s
Test / Flake checks (push) Successful in 1m30s
These names are less ambiguous and should be understandable without reading the source code.

Signed-off-by: Ophestra <cat@gensokyo.uk>
2025-10-21 01:49:36 +09:00
55 changed files with 1649 additions and 885 deletions

View File

@@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"log" "log"
@@ -51,7 +50,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
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 { app.Shim(msg); return errSuccess })
c.Command("app", "Load and start container from configuration file", func(args []string) error { c.Command("app", "Load and start container from configuration file", func(args []string) error {
if len(args) < 1 { if len(args) < 1 {
@@ -227,8 +226,11 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
} else { } else {
if f, err := os.Open(flagDBusConfigSession); err != nil { if f, err := os.Open(flagDBusConfigSession); err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
} else if err = json.NewDecoder(f).Decode(&config.SessionBus); err != nil { } else {
log.Fatalf("cannot load session bus proxy config from %q: %s", flagDBusConfigSession, err) decodeJSON(log.Fatal, "load session bus proxy config", f, &config.SessionBus)
if err = f.Close(); err != nil {
log.Fatal(err.Error())
}
} }
} }
@@ -236,8 +238,11 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
if flagDBusConfigSystem != "nil" { if flagDBusConfigSystem != "nil" {
if f, err := os.Open(flagDBusConfigSystem); err != nil { if f, err := os.Open(flagDBusConfigSystem); err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())
} else if err = json.NewDecoder(f).Decode(&config.SystemBus); err != nil { } else {
log.Fatalf("cannot load system bus proxy config from %q: %s", flagDBusConfigSystem, err) decodeJSON(log.Fatal, "load system bus proxy config", f, &config.SystemBus)
if err = f.Close(); err != nil {
log.Fatal(err.Error())
}
} }
} }
@@ -323,7 +328,7 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr
c.Command("version", "Display version information", func(args []string) error { fmt.Println(internal.Version()); return errSuccess }) c.Command("version", "Display version information", func(args []string) error { fmt.Println(internal.Version()); return errSuccess })
c.Command("license", "Show full license text", func(args []string) error { fmt.Println(license); return errSuccess }) c.Command("license", "Show full license text", func(args []string) error { fmt.Println(license); return errSuccess })
c.Command("template", "Produce a config template", func(args []string) error { printJSON(os.Stdout, false, hst.Template()); return errSuccess }) c.Command("template", "Produce a config template", func(args []string) error { encodeJSON(log.Fatal, os.Stdout, false, hst.Template()); return errSuccess })
c.Command("help", "Show this help message", func([]string) error { c.PrintHelp(); return errSuccess }) c.Command("help", "Show this help message", func([]string) error { c.PrintHelp(); return errSuccess })
return c return c

60
cmd/hakurei/json.go Normal file
View File

@@ -0,0 +1,60 @@
package main
import (
"encoding/json"
"errors"
"io"
"strconv"
)
// decodeJSON decodes json from r and stores it in v. A non-nil error results in a call to fatal.
func decodeJSON(fatal func(v ...any), op string, r io.Reader, v any) {
err := json.NewDecoder(r).Decode(v)
if err == nil {
return
}
var (
syntaxError *json.SyntaxError
unmarshalTypeError *json.UnmarshalTypeError
msg string
)
switch {
case errors.As(err, &syntaxError) && syntaxError != nil:
msg = syntaxError.Error() +
" at byte " + strconv.FormatInt(syntaxError.Offset, 10)
case errors.As(err, &unmarshalTypeError) && unmarshalTypeError != nil:
msg = "inappropriate " + unmarshalTypeError.Value +
" at byte " + strconv.FormatInt(unmarshalTypeError.Offset, 10)
default:
// InvalidUnmarshalError: incorrect usage, does not need to be handled
// io.ErrUnexpectedEOF: no additional error information available
msg = err.Error()
}
fatal("cannot " + op + ": " + msg)
}
// encodeJSON encodes v to output. A non-nil error results in a call to fatal.
func encodeJSON(fatal func(v ...any), output io.Writer, short bool, v any) {
encoder := json.NewEncoder(output)
if !short {
encoder.SetIndent("", " ")
}
if err := encoder.Encode(v); err != nil {
var marshalerError *json.MarshalerError
if errors.As(err, &marshalerError) && marshalerError != nil {
// this likely indicates an implementation error in hst
fatal("cannot encode json for " + marshalerError.Type.String() + ": " + marshalerError.Err.Error())
return
}
// UnsupportedTypeError, UnsupportedValueError: incorrect usage, does not need to be handled
fatal("cannot write json: " + err.Error())
}
}

107
cmd/hakurei/json_test.go Normal file
View File

@@ -0,0 +1,107 @@
package main_test
import (
"io"
"reflect"
"strings"
"testing"
_ "unsafe"
"hakurei.app/container/stub"
)
//go:linkname decodeJSON hakurei.app/cmd/hakurei.decodeJSON
func decodeJSON(fatal func(v ...any), op string, r io.Reader, v any)
func TestDecodeJSON(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
t reflect.Type
data string
want any
msg string
}{
{"success", reflect.TypeFor[uintptr](), "3735928559\n", uintptr(0xdeadbeef), ""},
{"syntax", reflect.TypeFor[*int](), "\x00", nil,
`cannot load sample: invalid character '\x00' looking for beginning of value at byte 1`},
{"type", reflect.TypeFor[uintptr](), "-1", nil,
`cannot load sample: inappropriate number -1 at byte 2`},
{"default", reflect.TypeFor[*int](), "{", nil,
"cannot load sample: unexpected EOF"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
var (
gotP = reflect.New(tc.t)
gotMsg *string
)
decodeJSON(func(v ...any) {
if gotMsg != nil {
t.Fatal("fatal called twice")
}
msg := v[0].(string)
gotMsg = &msg
}, "load sample", strings.NewReader(tc.data), gotP.Interface())
if tc.msg != "" {
if gotMsg == nil {
t.Errorf("decodeJSON: success, want fatal %q", tc.msg)
} else if *gotMsg != tc.msg {
t.Errorf("decodeJSON: fatal = %q, want %q", *gotMsg, tc.msg)
}
} else if gotMsg != nil {
t.Errorf("decodeJSON: fatal = %q", *gotMsg)
} else if !reflect.DeepEqual(gotP.Elem().Interface(), tc.want) {
t.Errorf("decodeJSON: %#v, want %#v", gotP.Elem().Interface(), tc.want)
}
})
}
}
//go:linkname encodeJSON hakurei.app/cmd/hakurei.encodeJSON
func encodeJSON(fatal func(v ...any), output io.Writer, short bool, v any)
func TestEncodeJSON(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
v any
want string
}{
{"marshaler", errorJSONMarshaler{},
`cannot encode json for main_test.errorJSONMarshaler: unique error 3735928559 injected by the test suite`},
{"default", func() {},
`cannot write json: json: unsupported type: func()`},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
var called bool
encodeJSON(func(v ...any) {
if called {
t.Fatal("fatal called twice")
}
called = true
if v[0].(string) != tc.want {
t.Errorf("encodeJSON: fatal = %q, want %q", v[0].(string), tc.want)
}
}, nil, false, tc.v)
if !called {
t.Errorf("encodeJSON: success, want fatal %q", tc.want)
}
})
}
}
// errorJSONMarshaler implements json.Marshaler.
type errorJSONMarshaler struct{}
func (errorJSONMarshaler) MarshalJSON() ([]byte, error) { return nil, stub.UniqueError(0xdeadbeef) }

View File

@@ -1,7 +1,6 @@
package main package main
import ( import (
"encoding/json"
"errors" "errors"
"io" "io"
"log" "log"
@@ -17,7 +16,7 @@ import (
) )
func tryPath(msg message.Msg, name string) (config *hst.Config) { func tryPath(msg message.Msg, name string) (config *hst.Config) {
var r io.Reader var r io.ReadCloser
config = new(hst.Config) config = new(hst.Config)
if name != "-" { if name != "-" {
@@ -26,26 +25,20 @@ func tryPath(msg message.Msg, name string) (config *hst.Config) {
msg.Verbose("load configuration from file") msg.Verbose("load configuration from file")
if f, err := os.Open(name); err != nil { if f, err := os.Open(name); err != nil {
log.Fatalf("cannot access configuration file %q: %s", name, err) log.Fatal(err.Error())
return
} else { } else {
// finalizer closes f
r = f r = f
} }
} else {
defer func() {
if err := r.(io.ReadCloser).Close(); err != nil {
log.Printf("cannot close config fd: %v", err)
}
}()
} }
} else { } else {
r = os.Stdin r = os.Stdin
} }
if err := json.NewDecoder(r).Decode(&config); err != nil { decodeJSON(log.Fatal, "load configuration", r, &config)
log.Fatalf("cannot load configuration: %v", err) if err := r.Close(); err != nil {
log.Fatal(err.Error())
} }
return return
} }

View File

@@ -1,7 +1,6 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"log" "log"
@@ -12,23 +11,26 @@ import (
"time" "time"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal"
"hakurei.app/internal/app" "hakurei.app/internal/app"
"hakurei.app/internal/app/state" "hakurei.app/internal/app/state"
"hakurei.app/message" "hakurei.app/message"
) )
// printShowSystem populates and writes a representation of [hst.Info] to output.
func printShowSystem(output io.Writer, short, flagJSON bool) { func printShowSystem(output io.Writer, short, flagJSON bool) {
t := newPrinter(output) t := newPrinter(output)
defer t.MustFlush() defer t.MustFlush()
info := &hst.Info{User: new(app.Hsu).MustID(nil)} info := &hst.Info{Version: internal.Version(), User: new(app.Hsu).MustID(nil)}
app.CopyPaths().Copy(&info.Paths, info.User) app.CopyPaths().Copy(&info.Paths, info.User)
if flagJSON { if flagJSON {
printJSON(output, short, info) encodeJSON(log.Fatal, output, short, info)
return return
} }
t.Printf("Version:\t%s\n", info.Version)
t.Printf("User:\t%d\n", info.User) t.Printf("User:\t%d\n", info.User)
t.Printf("TempDir:\t%s\n", info.TempDir) t.Printf("TempDir:\t%s\n", info.TempDir)
t.Printf("SharePath:\t%s\n", info.SharePath) t.Printf("SharePath:\t%s\n", info.SharePath)
@@ -36,6 +38,7 @@ func printShowSystem(output io.Writer, short, flagJSON bool) {
t.Printf("RunDirPath:\t%s\n", info.RunDirPath) t.Printf("RunDirPath:\t%s\n", info.RunDirPath)
} }
// printShowInstance writes a representation of [state.State] or [hst.Config] to output.
func printShowInstance( func printShowInstance(
output io.Writer, now time.Time, output io.Writer, now time.Time,
instance *state.State, config *hst.Config, instance *state.State, config *hst.Config,
@@ -44,9 +47,9 @@ func printShowInstance(
if flagJSON { if flagJSON {
if instance != nil { if instance != nil {
printJSON(output, short, instance) encodeJSON(log.Fatal, output, short, instance)
} else { } else {
printJSON(output, short, config) encodeJSON(log.Fatal, output, short, config)
} }
return return
} }
@@ -86,24 +89,19 @@ func printShowInstance(
if params.Hostname != "" { if params.Hostname != "" {
t.Printf(" Hostname:\t%s\n", params.Hostname) t.Printf(" Hostname:\t%s\n", params.Hostname)
} }
flags := make([]string, 0, 7) flags := params.Flags.String()
writeFlag := func(name string, flag uintptr, force bool) {
if params.Flags&flag != 0 || force { // this is included in the upper hst.Config struct but is relevant here
flags = append(flags, name) const flagDirectWayland = "directwl"
if config.DirectWayland {
// hardcoded value when every flag is unset
if flags == "none" {
flags = flagDirectWayland
} else {
flags += ", " + flagDirectWayland
} }
} }
writeFlag("userns", hst.FUserns, false) t.Printf(" Flags:\t%s\n", flags)
writeFlag("devel", hst.FDevel, false)
writeFlag("net", hst.FHostNet, false)
writeFlag("abstract", hst.FHostAbstract, false)
writeFlag("device", hst.FDevice, false)
writeFlag("tty", hst.FTty, false)
writeFlag("mapuid", hst.FMapRealUID, false)
writeFlag("directwl", 0, config.DirectWayland)
if len(flags) == 0 {
flags = append(flags, "none")
}
t.Printf(" Flags:\t%s\n", strings.Join(flags, " "))
if params.Path != nil { if params.Path != nil {
t.Printf(" Path:\t%s\n", params.Path) t.Printf(" Path:\t%s\n", params.Path)
@@ -168,6 +166,7 @@ func printShowInstance(
return return
} }
// printPs writes a representation of active instances to output.
func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) { func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) {
var entries state.Entries var entries state.Entries
if e, err := state.Join(s); err != nil { if e, err := state.Join(s); err != nil {
@@ -184,7 +183,7 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
for id, instance := range entries { for id, instance := range entries {
es[id.String()] = instance es[id.String()] = instance
} }
printJSON(output, short, es) encodeJSON(log.Fatal, output, short, es)
return return
} }
@@ -213,7 +212,7 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
for i, e := range exp { for i, e := range exp {
v[i] = e.s v[i] = e.s
} }
printJSON(output, short, v) encodeJSON(log.Fatal, output, short, v)
} else { } else {
for _, e := range exp { for _, e := range exp {
mustPrintln(output, e.s[:8]) mustPrintln(output, e.s[:8])
@@ -247,40 +246,39 @@ func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON boo
} }
} }
// expandedStateEntry stores [state.State] alongside a string representation of its [state.ID].
type expandedStateEntry struct { type expandedStateEntry struct {
s string s string
*state.State *state.State
} }
func printJSON(output io.Writer, short bool, v any) { // newPrinter returns a configured, wrapped [tabwriter.Writer].
encoder := json.NewEncoder(output)
if !short {
encoder.SetIndent("", " ")
}
if err := encoder.Encode(v); err != nil {
log.Fatalf("cannot serialise: %v", err)
}
}
func newPrinter(output io.Writer) *tp { return &tp{tabwriter.NewWriter(output, 0, 1, 4, ' ', 0)} } func newPrinter(output io.Writer) *tp { return &tp{tabwriter.NewWriter(output, 0, 1, 4, ' ', 0)} }
// tp wraps [tabwriter.Writer] to provide additional formatting methods.
type tp struct{ *tabwriter.Writer } type tp struct{ *tabwriter.Writer }
// Printf calls [fmt.Fprintf] on the underlying [tabwriter.Writer].
func (p *tp) Printf(format string, a ...any) { func (p *tp) Printf(format string, a ...any) {
if _, err := fmt.Fprintf(p, format, a...); err != nil { if _, err := fmt.Fprintf(p, format, a...); err != nil {
log.Fatalf("cannot write to tabwriter: %v", err) log.Fatalf("cannot write to tabwriter: %v", err)
} }
} }
// Println calls [fmt.Fprintln] on the underlying [tabwriter.Writer].
func (p *tp) Println(a ...any) { func (p *tp) Println(a ...any) {
if _, err := fmt.Fprintln(p, a...); err != nil { if _, err := fmt.Fprintln(p, a...); err != nil {
log.Fatalf("cannot write to tabwriter: %v", err) log.Fatalf("cannot write to tabwriter: %v", err)
} }
} }
// MustFlush calls the Flush method of [tabwriter.Writer] and calls [log.Fatalf] on a non-nil error.
func (p *tp) MustFlush() { func (p *tp) MustFlush() {
if err := p.Writer.Flush(); err != nil { if err := p.Writer.Flush(); err != nil {
log.Fatalf("cannot flush tabwriter: %v", err) log.Fatalf("cannot flush tabwriter: %v", err)
} }
} }
func mustPrint(output io.Writer, a ...any) { func mustPrint(output io.Writer, a ...any) {
if _, err := fmt.Fprint(output, a...); err != nil { if _, err := fmt.Fprint(output, a...); err != nil {
log.Fatalf("cannot print: %v", err) log.Fatalf("cannot print: %v", err)

View File

@@ -43,7 +43,7 @@ func TestPrintShowInstance(t *testing.T) {
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: userns devel net abstract device tty mapuid Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland
@@ -87,6 +87,22 @@ App
Enablements: (no enablements) Enablements: (no enablements)
Flags: none Flags: none
`, false},
{"config flag none directwl", nil, &hst.Config{DirectWayland: true, Container: new(hst.ContainerConfig)}, false, false, `Error: container configuration missing path to home directory!
App
Identity: 0
Enablements: (no enablements)
Flags: directwl
`, false},
{"config flag directwl", nil, &hst.Config{DirectWayland: true, Container: &hst.ContainerConfig{Flags: hst.FMultiarch}}, false, false, `Error: container configuration missing path to home directory!
App
Identity: 0
Enablements: (no enablements)
Flags: multiarch, directwl
`, false}, `, false},
{"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]hst.ExtraPermConfig, 1)}, false, false, `Error: container configuration missing path to home directory! {"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]hst.ExtraPermConfig, 1)}, false, false, `Error: container configuration missing path to home directory!
@@ -124,7 +140,7 @@ App
Groups: video, dialout, plugdev Groups: video, dialout, plugdev
Home: /data/data/org.chromium.Chromium Home: /data/data/org.chromium.Chromium
Hostname: localhost Hostname: localhost
Flags: userns devel net abstract device tty mapuid Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir
Path: /run/current-system/sw/bin/chromium Path: /run/current-system/sw/bin/chromium
Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland Arguments: chromium --ignore-gpu-blocklist --disable-smooth-scrolling --enable-features=UseOzonePlatform --ozone-platform=wayland

View File

@@ -14,7 +14,7 @@ import (
"hakurei.app/message" "hakurei.app/message"
) )
var hakureiPath = internal.MustHakureiPath() var hakureiPathVal = internal.MustHakureiPath().String()
func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) { func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, beforeFail func()) {
var ( var (
@@ -27,9 +27,9 @@ func mustRunApp(ctx context.Context, msg message.Msg, config *hst.Config, before
log.Fatalf("cannot pipe: %v", err) log.Fatalf("cannot pipe: %v", err)
} else { } else {
if msg.IsVerbose() { if msg.IsVerbose() {
cmd = exec.CommandContext(ctx, hakureiPath.String(), "-v", "app", "3") cmd = exec.CommandContext(ctx, hakureiPathVal, "-v", "app", "3")
} else { } else {
cmd = exec.CommandContext(ctx, hakureiPath.String(), "app", "3") cmd = exec.CommandContext(ctx, hakureiPathVal, "app", "3")
} }
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
cmd.ExtraFiles = []*os.File{r} cmd.ExtraFiles = []*os.File{r}

View File

@@ -8,6 +8,7 @@ import (
"log" "log"
"os" "os"
"path" "path"
"runtime"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@@ -26,7 +27,14 @@ const (
identityMax = 9999 identityMax = 9999
) )
// hakureiPath is the absolute path to Hakurei.
//
// This is set by the linker.
var hakureiPath string
func main() { func main() {
runtime.LockOSThread()
log.SetFlags(0) log.SetFlags(0)
log.SetPrefix("hsu: ") log.SetPrefix("hsu: ")
log.SetOutput(os.Stderr) log.SetOutput(os.Stderr)
@@ -43,13 +51,18 @@ func main() {
log.Fatal("this program must not be started by root") log.Fatal("this program must not be started by root")
} }
if !path.IsAbs(hakureiPath) {
log.Fatal("this program is compiled incorrectly")
return
}
var toolPath string var toolPath string
pexe := path.Join("/proc", strconv.Itoa(os.Getppid()), "exe") pexe := path.Join("/proc", strconv.Itoa(os.Getppid()), "exe")
if p, err := os.Readlink(pexe); err != nil { if p, err := os.Readlink(pexe); err != nil {
log.Fatalf("cannot read parent executable path: %v", err) log.Fatalf("cannot read parent executable path: %v", err)
} else if strings.HasSuffix(p, " (deleted)") { } else if strings.HasSuffix(p, " (deleted)") {
log.Fatal("hakurei executable has been deleted") log.Fatal("hakurei executable has been deleted")
} else if p != mustCheckPath(hmain) { } else if p != hakureiPath {
log.Fatal("this program must be started by hakurei") log.Fatal("this program must be started by hakurei")
} else { } else {
toolPath = p toolPath = p

View File

@@ -19,5 +19,5 @@ buildGoModule {
ldflags = lib.attrsets.foldlAttrs ( ldflags = lib.attrsets.foldlAttrs (
ldflags: name: value: ldflags: name: value:
ldflags ++ [ "-X main.${name}=${value}" ] ldflags ++ [ "-X main.${name}=${value}" ]
) [ "-s -w" ] { hmain = "${hakurei}/libexec/hakurei"; }; ) [ "-s -w" ] { hakureiPath = "${hakurei}/libexec/hakurei"; };
} }

View File

@@ -36,7 +36,7 @@ func TestParseUint32Fast(t *testing.T) {
} }
}) })
t.Run("full range", func(t *testing.T) { t.Run("range", func(t *testing.T) {
t.Parallel() t.Parallel()
testRange := func(i, end int) { testRange := func(i, end int) {
@@ -61,11 +61,9 @@ func TestParseUint32Fast(t *testing.T) {
} }
} }
testRange(0, 5000) testRange(0, 2500)
testRange(105000, 110000) testRange(23002500, 23005000)
testRange(23005000, 23010000) testRange(7890002500, 7890005000)
testRange(456005000, 456010000)
testRange(7890005000, 7890010000)
}) })
} }

View File

@@ -1,20 +0,0 @@
package main
import (
"log"
"path"
)
const compPoison = "INVALIDINVALIDINVALIDINVALIDINVALID"
var (
hmain = compPoison
)
func mustCheckPath(p string) string {
if p != compPoison && p != "" && path.IsAbs(p) {
return p
}
log.Fatal("this program is compiled incorrectly")
return compPoison
}

View File

@@ -5,8 +5,8 @@ import (
"os" "os"
"testing" "testing"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/message" "hakurei.app/message"
) )
@@ -23,14 +23,14 @@ func TestAutoRootOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{ {"readdir", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)), call("readdir", stub.ExpectArgs{"/"}, stubDir(), stub.UniqueError(2)),
}, stub.UniqueError(2), nil, nil}, }, stub.UniqueError(2), nil, nil},
{"early", &Params{ParentPerm: 0750}, &AutoRootOp{ {"early", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@@ -39,7 +39,7 @@ func TestAutoRootOp(t *testing.T) {
{"apply", &Params{ParentPerm: 0750}, &AutoRootOp{ {"apply", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@@ -60,7 +60,7 @@ func TestAutoRootOp(t *testing.T) {
{"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{ {"success pd", &Params{ParentPerm: 0750}, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, []stub.Call{ }, []stub.Call{
call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64", call("readdir", stub.ExpectArgs{"/"}, stubDir("bin", "dev", "etc", "home", "lib64",
"lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil), "lost+found", "mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var"), nil),
@@ -127,10 +127,10 @@ func TestAutoRootOp(t *testing.T) {
}) })
checkOpsBuilder(t, []opsBuilderTestCase{ checkOpsBuilder(t, []opsBuilderTestCase{
{"pd", new(Ops).Root(check.MustAbs("/"), bits.BindWritable), Ops{ {"pd", new(Ops).Root(check.MustAbs("/"), comp.BindWritable), Ops{
&AutoRootOp{ &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, },
}}, }},
}) })
@@ -140,42 +140,42 @@ func TestAutoRootOp(t *testing.T) {
{"internal ne", &AutoRootOp{ {"internal ne", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
resolved: []*BindMountOp{new(BindMountOp)}, resolved: []*BindMountOp{new(BindMountOp)},
}, true}, }, true},
{"flags differs", &AutoRootOp{ {"flags differs", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, false}, }, false},
{"host differs", &AutoRootOp{ {"host differs", &AutoRootOp{
Host: check.MustAbs("/tmp/"), Host: check.MustAbs("/tmp/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, false}, }, false},
{"equals", &AutoRootOp{ {"equals", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, &AutoRootOp{ }, &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, true}, }, true},
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"root", &AutoRootOp{ {"root", &AutoRootOp{
Host: check.MustAbs("/"), Host: check.MustAbs("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}, "setting up", `auto root "/" flags 0x2`}, }, "setting up", `auto root "/" flags 0x2`},
}) })
} }

View File

@@ -1,13 +0,0 @@
// Package bits contains constants for configuring the container.
package bits
const (
// BindOptional skips nonexistent host paths.
BindOptional = 1 << iota
// BindWritable mounts filesystem read-write.
BindWritable
// BindDevice allows access to devices (special files) on this filesystem.
BindDevice
// BindEnsure attempts to create the host path if it does not exist.
BindEnsure
)

View File

@@ -49,41 +49,10 @@ func capset(hdrp *capHeader, datap *[2]capData) error {
} }
// capBoundingSetDrop drops a capability from the calling thread's capability bounding set. // capBoundingSetDrop drops a capability from the calling thread's capability bounding set.
func capBoundingSetDrop(cap uintptr) error { func capBoundingSetDrop(cap uintptr) error { return Prctl(syscall.PR_CAPBSET_DROP, cap, 0) }
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
syscall.PR_CAPBSET_DROP,
cap, 0,
)
if r != 0 {
return errno
}
return nil
}
// capAmbientClearAll clears the ambient capability set of the calling thread. // capAmbientClearAll clears the ambient capability set of the calling thread.
func capAmbientClearAll() error { func capAmbientClearAll() error { return Prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0) }
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
PR_CAP_AMBIENT,
PR_CAP_AMBIENT_CLEAR_ALL, 0,
)
if r != 0 {
return errno
}
return nil
}
// capAmbientRaise adds to the ambient capability set of the calling thread. // capAmbientRaise adds to the ambient capability set of the calling thread.
func capAmbientRaise(cap uintptr) error { func capAmbientRaise(cap uintptr) error { return Prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap) }
r, _, errno := syscall.Syscall(
syscall.SYS_PRCTL,
PR_CAP_AMBIENT,
PR_CAP_AMBIENT_RAISE,
cap,
)
if r != 0 {
return errno
}
return nil
}

View File

@@ -1,4 +1,16 @@
package bits // Package comp contains constants from container packages without depending on cgo.
package comp
const (
// BindOptional skips nonexistent host paths.
BindOptional = 1 << iota
// BindWritable mounts filesystem read-write.
BindWritable
// BindDevice allows access to devices (special files) on this filesystem.
BindDevice
// BindEnsure attempts to create the host path if it does not exist.
BindEnsure
)
// FilterPreset specifies parts of the syscall filter preset to enable. // FilterPreset specifies parts of the syscall filter preset to enable.
type FilterPreset int type FilterPreset int

View File

@@ -14,8 +14,8 @@ import (
. "syscall" . "syscall"
"time" "time"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/message" "hakurei.app/message"
@@ -86,7 +86,7 @@ type (
// Extra seccomp flags. // Extra seccomp flags.
SeccompFlags seccomp.ExportFlag SeccompFlags seccomp.ExportFlag
// Seccomp presets. Has no effect unless SeccompRules is zero-length. // Seccomp presets. Has no effect unless SeccompRules is zero-length.
SeccompPresets bits.FilterPreset SeccompPresets comp.FilterPreset
// Do not load seccomp program. // Do not load seccomp program.
SeccompDisable bool SeccompDisable bool
@@ -174,7 +174,7 @@ func (p *Container) Start() error {
} }
if !p.RetainSession { if !p.RetainSession {
p.SeccompPresets |= bits.PresetDenyTTY p.SeccompPresets |= comp.PresetDenyTTY
} }
if p.AdoptWaitDelay == 0 { if p.AdoptWaitDelay == 0 {

View File

@@ -20,8 +20,8 @@ import (
"hakurei.app/command" "hakurei.app/command"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/vfs" "hakurei.app/container/vfs"
"hakurei.app/hst" "hakurei.app/hst"
@@ -206,20 +206,20 @@ var containerTestCases = []struct {
rules []seccomp.NativeRule rules []seccomp.NativeRule
flags seccomp.ExportFlag flags seccomp.ExportFlag
presets bits.FilterPreset presets comp.FilterPreset
}{ }{
{"minimal", true, false, false, true, {"minimal", true, false, false, true,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1000, 100, nil, 0, bits.PresetStrict}, 1000, 100, nil, 0, comp.PresetStrict},
{"allow", true, true, true, false, {"allow", true, true, true, false,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1000, 100, nil, 0, bits.PresetExt | bits.PresetDenyDevel}, 1000, 100, nil, 0, comp.PresetExt | comp.PresetDenyDevel},
{"no filter", false, true, true, true, {"no filter", false, true, true, true,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1000, 100, nil, 0, bits.PresetExt}, 1000, 100, nil, 0, comp.PresetExt},
{"custom rules", true, true, true, false, {"custom rules", true, true, true, false,
emptyOps, emptyMnt, emptyOps, emptyMnt,
1, 31, []seccomp.NativeRule{{Syscall: seccomp.ScmpSyscall(syscall.SYS_SETUID), Errno: seccomp.ScmpErrno(syscall.EPERM)}}, 0, bits.PresetExt}, 1, 31, []seccomp.NativeRule{{Syscall: seccomp.ScmpSyscall(syscall.SYS_SETUID), Errno: seccomp.ScmpErrno(syscall.EPERM)}}, 0, comp.PresetExt},
{"tmpfs", true, false, false, true, {"tmpfs", true, false, false, true,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
@@ -228,7 +228,7 @@ var containerTestCases = []struct {
earlyMnt( earlyMnt(
ent("/", hst.PrivateTmp, "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", ignore), ent("/", hst.PrivateTmp, "rw,nosuid,nodev,relatime", "tmpfs", "ephemeral", ignore),
), ),
9, 9, nil, 0, bits.PresetStrict}, 9, 9, nil, 0, comp.PresetStrict},
{"dev", true, true /* go test output is not a tty */, false, false, {"dev", true, true /* go test output is not a tty */, false, false,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
@@ -246,7 +246,7 @@ var containerTestCases = []struct {
ent("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"), ent("/", "/dev/mqueue", "rw,nosuid,nodev,noexec,relatime", "mqueue", "mqueue", "rw"),
ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore), ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
), ),
1971, 100, nil, 0, bits.PresetStrict}, 1971, 100, nil, 0, comp.PresetStrict},
{"dev no mqueue", true, true /* go test output is not a tty */, false, false, {"dev no mqueue", true, true /* go test output is not a tty */, false, false,
earlyOps(new(container.Ops). earlyOps(new(container.Ops).
@@ -263,7 +263,7 @@ var containerTestCases = []struct {
ent("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"), ent("/", "/dev/pts", "rw,nosuid,noexec,relatime", "devpts", "devpts", "rw,mode=620,ptmxmode=666"),
ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore), ent("/", "/dev/shm", "rw,nosuid,nodev,relatime", "tmpfs", "tmpfs", ignore),
), ),
1971, 100, nil, 0, bits.PresetStrict}, 1971, 100, nil, 0, comp.PresetStrict},
{"overlay", true, false, false, true, {"overlay", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
@@ -300,7 +300,7 @@ var containerTestCases = []struct {
",redirect_dir=nofollow,uuid=on,userxattr"), ",redirect_dir=nofollow,uuid=on,userxattr"),
} }
}, },
1 << 3, 1 << 14, nil, 0, bits.PresetStrict}, 1 << 3, 1 << 14, nil, 0, comp.PresetStrict},
{"overlay ephemeral", true, false, false, true, {"overlay ephemeral", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
@@ -324,7 +324,7 @@ var containerTestCases = []struct {
ent("/", hst.PrivateTmp, "rw", "overlay", "overlay", ignore), ent("/", hst.PrivateTmp, "rw", "overlay", "overlay", ignore),
} }
}, },
1 << 3, 1 << 14, nil, 0, bits.PresetStrict}, 1 << 3, 1 << 14, nil, 0, comp.PresetStrict},
{"overlay readonly", true, false, false, true, {"overlay readonly", true, false, false, true,
func(t *testing.T) (*container.Ops, context.Context) { func(t *testing.T) (*container.Ops, context.Context) {
@@ -352,7 +352,7 @@ var containerTestCases = []struct {
",redirect_dir=nofollow,userxattr"), ",redirect_dir=nofollow,userxattr"),
} }
}, },
1 << 3, 1 << 14, nil, 0, bits.PresetStrict}, 1 << 3, 1 << 14, nil, 0, comp.PresetStrict},
} }
func TestContainer(t *testing.T) { func TestContainer(t *testing.T) {
@@ -560,9 +560,9 @@ func TestContainerString(t *testing.T) {
c := container.NewCommand(t.Context(), msg, check.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env") c := container.NewCommand(t.Context(), msg, check.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env")
c.SeccompFlags |= seccomp.AllowMultiarch c.SeccompFlags |= seccomp.AllowMultiarch
c.SeccompRules = seccomp.Preset( c.SeccompRules = seccomp.Preset(
bits.PresetExt|bits.PresetDenyNS|bits.PresetDenyTTY, comp.PresetExt|comp.PresetDenyNS|comp.PresetDenyTTY,
c.SeccompFlags) c.SeccompFlags)
c.SeccompPresets = bits.PresetStrict c.SeccompPresets = comp.PresetStrict
want := `argv: ["ldd" "/usr/bin/env"], filter: true, rules: 65, flags: 0x1, presets: 0xf` want := `argv: ["ldd" "/usr/bin/env"], filter: true, rules: 65, flags: 0x1, presets: 0xf`
if got := c.String(); got != want { if got := c.String(); got != want {
t.Errorf("String: %s, want %s", got, want) t.Errorf("String: %s, want %s", got, want)

View File

@@ -96,16 +96,15 @@ type initParams struct {
} }
// Init is called by [TryArgv0] if the current process is the container init. // Init is called by [TryArgv0] if the current process is the container init.
func Init(msg message.Msg) { func Init(msg message.Msg) { initEntrypoint(direct{}, msg) }
if msg == nil {
panic("attempting to call initEntrypoint with nil msg")
}
initEntrypoint(direct{}, msg)
}
func initEntrypoint(k syscallDispatcher, msg message.Msg) { func initEntrypoint(k syscallDispatcher, msg message.Msg) {
k.lockOSThread() k.lockOSThread()
if msg == nil {
panic("attempting to call initEntrypoint with nil msg")
}
if k.getpid() != 1 { if k.getpid() != 1 {
k.fatal(msg, "this process must run as pid 1") k.fatal(msg, "this process must run as pid 1")
} }
@@ -451,6 +450,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
} }
} }
// initName is the prefix used by log.std in the init process.
const initName = "init" const initName = "init"
// TryArgv0 calls [Init] if the last element of argv0 is "init". // TryArgv0 calls [Init] if the last element of argv0 is "init".

View File

@@ -6,8 +6,8 @@ import (
"testing" "testing"
"time" "time"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@@ -70,7 +70,7 @@ func TestInitEntrypoint(t *testing.T) {
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(79), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(79), nil),
@@ -95,7 +95,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(78), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(78), nil),
@@ -123,7 +123,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(76), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(76), nil),
@@ -152,7 +152,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(74), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(74), nil),
@@ -182,7 +182,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(72), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(72), nil),
@@ -213,7 +213,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(70), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(70), nil),
@@ -245,7 +245,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(68), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(68), nil),
@@ -279,7 +279,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(66), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(66), nil),
@@ -315,7 +315,7 @@ func TestInitEntrypoint(t *testing.T) {
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: (*Ops)(sliceAddr(make(Ops, 1))), Ops: (*Ops)(sliceAddr(make(Ops, 1))),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(64), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(64), nil),
@@ -351,9 +351,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(nil, nil, bits.BindDevice), Ops: new(Ops).Bind(nil, nil, comp.BindDevice),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(63), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(63), nil),
@@ -389,9 +389,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(62), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(62), nil),
@@ -428,9 +428,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(60), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(60), nil),
@@ -467,9 +467,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(59), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(59), nil),
@@ -507,9 +507,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(57), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(57), nil),
@@ -548,9 +548,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(55), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(55), nil),
@@ -590,9 +590,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(53), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(53), nil),
@@ -633,9 +633,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(51), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(51), nil),
@@ -677,9 +677,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(49), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(49), nil),
@@ -722,9 +722,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(47), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(47), nil),
@@ -768,9 +768,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(45), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(45), nil),
@@ -823,9 +823,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(43), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(43), nil),
@@ -878,9 +878,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(42), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(42), nil),
@@ -934,9 +934,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(40), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(40), nil),
@@ -991,9 +991,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(38), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(38), nil),
@@ -1029,8 +1029,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, stub.UniqueError(37)), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, stub.UniqueError(37)),
call("fatalf", stub.ExpectArgs{"cannot open intermediate root: %v", []any{stub.UniqueError(37)}}, nil, nil), call("fatalf", stub.ExpectArgs{"cannot open intermediate root: %v", []any{stub.UniqueError(37)}}, nil, nil),
}, },
}, nil}, }, nil},
@@ -1050,9 +1050,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(36), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(36), nil),
@@ -1088,8 +1088,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, stub.UniqueError(35)), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, stub.UniqueError(35)),
call("fatalf", stub.ExpectArgs{"cannot enter sysroot: %v", []any{stub.UniqueError(35)}}, nil, nil), call("fatalf", stub.ExpectArgs{"cannot enter sysroot: %v", []any{stub.UniqueError(35)}}, nil, nil),
}, },
@@ -1110,9 +1110,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(34), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(34), nil),
@@ -1148,8 +1148,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, stub.UniqueError(33)), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, stub.UniqueError(33)),
call("fatalf", stub.ExpectArgs{"cannot pivot into sysroot: %v", []any{stub.UniqueError(33)}}, nil, nil), call("fatalf", stub.ExpectArgs{"cannot pivot into sysroot: %v", []any{stub.UniqueError(33)}}, nil, nil),
@@ -1171,9 +1171,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(32), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(32), nil),
@@ -1209,8 +1209,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, stub.UniqueError(31)), call("fchdir", stub.ExpectArgs{1 << 35}, nil, stub.UniqueError(31)),
@@ -1233,9 +1233,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(30), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(30), nil),
@@ -1271,8 +1271,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1296,9 +1296,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(28), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(28), nil),
@@ -1334,8 +1334,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1360,9 +1360,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(26), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(26), nil),
@@ -1398,8 +1398,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1425,9 +1425,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(24), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(24), nil),
@@ -1463,8 +1463,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1491,9 +1491,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(22), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(22), nil),
@@ -1529,8 +1529,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1565,9 +1565,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(20), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(20), nil),
@@ -1603,8 +1603,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1672,9 +1672,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(18), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(18), nil),
@@ -1710,8 +1710,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1780,9 +1780,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(16), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(16), nil),
@@ -1818,8 +1818,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -1869,7 +1869,7 @@ func TestInitEntrypoint(t *testing.T) {
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil), call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil), call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil), call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil),
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{bits.FilterPreset(0xf)}}, nil, nil), call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{comp.FilterPreset(0xf)}}, nil, nil),
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, stub.UniqueError(15)), call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, stub.UniqueError(15)),
call("fatalf", stub.ExpectArgs{"cannot load syscall filter: %v", []any{stub.UniqueError(15)}}, nil, nil), call("fatalf", stub.ExpectArgs{"cannot load syscall filter: %v", []any{stub.UniqueError(15)}}, nil, nil),
}, },
@@ -1890,7 +1890,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -1927,8 +1927,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2004,7 +2004,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2041,8 +2041,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2106,7 +2106,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2143,8 +2143,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2199,7 +2199,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2236,8 +2236,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2294,7 +2294,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2331,8 +2331,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2396,7 +2396,7 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 24, Uid: 1 << 24,
Gid: 1 << 47, Gid: 1 << 47,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompDisable: true, SeccompDisable: true,
ParentPerm: 0750, ParentPerm: 0750,
@@ -2433,8 +2433,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2534,9 +2534,9 @@ func TestInitEntrypoint(t *testing.T) {
Uid: 1 << 32, Uid: 1 << 32,
Gid: 1 << 31, Gid: 1 << 31,
Hostname: "hakurei-check", Hostname: "hakurei-check",
Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), bits.BindDevice).Proc(check.MustAbs("/proc/")), Ops: new(Ops).Bind(check.MustAbs("/"), check.MustAbs("/"), comp.BindDevice).Proc(check.MustAbs("/proc/")),
SeccompRules: make([]seccomp.NativeRule, 0), SeccompRules: make([]seccomp.NativeRule, 0),
SeccompPresets: bits.PresetStrict, SeccompPresets: comp.PresetStrict,
RetainSession: true, RetainSession: true,
Privileged: true, Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(0), nil), }, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(0), nil),
@@ -2572,8 +2572,8 @@ func TestInitEntrypoint(t *testing.T) {
/* end apply */ /* end apply */
call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil), call("mount", stub.ExpectArgs{"host", "host", "", uintptr(0x4c000), ""}, nil, nil),
call("unmount", stub.ExpectArgs{"host", 2}, nil, nil), call("unmount", stub.ExpectArgs{"host", 2}, nil, nil),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, syscall.EINTR), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, syscall.EINTR),
call("open", stub.ExpectArgs{"/", 0x10000, uint32(0)}, 1<<35, nil), call("open", stub.ExpectArgs{"/", syscall.O_DIRECTORY | syscall.O_RDONLY, uint32(0)}, 1<<35, nil),
call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil), call("chdir", stub.ExpectArgs{"/sysroot"}, nil, nil),
call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil), call("pivotRoot", stub.ExpectArgs{".", "."}, nil, nil),
call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil), call("fchdir", stub.ExpectArgs{1 << 35}, nil, nil),
@@ -2623,7 +2623,7 @@ func TestInitEntrypoint(t *testing.T) {
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil), call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil), call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil), call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil),
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{bits.FilterPreset(0xf)}}, nil, nil), call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{comp.FilterPreset(0xf)}}, nil, nil),
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil), call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil),
call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil), call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil),
call("newFile", stub.ExpectArgs{uintptr(10), "extra file 0"}, (*os.File)(nil), nil), call("newFile", stub.ExpectArgs{uintptr(10), "extra file 0"}, (*os.File)(nil), nil),

View File

@@ -6,8 +6,8 @@ import (
"os" "os"
"syscall" "syscall"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
) )
func init() { gob.Register(new(BindMountOp)) } func init() { gob.Register(new(BindMountOp)) }
@@ -29,18 +29,18 @@ type BindMountOp struct {
func (b *BindMountOp) Valid() bool { func (b *BindMountOp) Valid() bool {
return b != nil && return b != nil &&
b.Source != nil && b.Target != nil && b.Source != nil && b.Target != nil &&
b.Flags&(bits.BindOptional|bits.BindEnsure) != (bits.BindOptional|bits.BindEnsure) b.Flags&(comp.BindOptional|comp.BindEnsure) != (comp.BindOptional|comp.BindEnsure)
} }
func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error { func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error {
if b.Flags&bits.BindEnsure != 0 { if b.Flags&comp.BindEnsure != 0 {
if err := k.mkdirAll(b.Source.String(), 0700); err != nil { if err := k.mkdirAll(b.Source.String(), 0700); err != nil {
return err return err
} }
} }
if pathname, err := k.evalSymlinks(b.Source.String()); err != nil { if pathname, err := k.evalSymlinks(b.Source.String()); err != nil {
if os.IsNotExist(err) && b.Flags&bits.BindOptional != 0 { if os.IsNotExist(err) && b.Flags&comp.BindOptional != 0 {
// leave sourceFinal as nil // leave sourceFinal as nil
return nil return nil
} }
@@ -53,7 +53,7 @@ func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error {
func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error { func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error {
if b.sourceFinal == nil { if b.sourceFinal == nil {
if b.Flags&bits.BindOptional == 0 { if b.Flags&comp.BindOptional == 0 {
// unreachable // unreachable
return OpStateError("bind") return OpStateError("bind")
} }
@@ -76,10 +76,10 @@ func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error {
} }
var flags uintptr = syscall.MS_REC var flags uintptr = syscall.MS_REC
if b.Flags&bits.BindWritable == 0 { if b.Flags&comp.BindWritable == 0 {
flags |= syscall.MS_RDONLY flags |= syscall.MS_RDONLY
} }
if b.Flags&bits.BindDevice == 0 { if b.Flags&comp.BindDevice == 0 {
flags |= syscall.MS_NODEV flags |= syscall.MS_NODEV
} }

View File

@@ -6,8 +6,8 @@ import (
"syscall" "syscall"
"testing" "testing"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
) )
@@ -25,7 +25,7 @@ func TestBindMountOp(t *testing.T) {
{"skip optional", new(Params), &BindMountOp{ {"skip optional", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: bits.BindOptional, Flags: comp.BindOptional,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "", syscall.ENOENT),
}, nil, nil, nil}, }, nil, nil, nil},
@@ -33,7 +33,7 @@ func TestBindMountOp(t *testing.T) {
{"success optional", new(Params), &BindMountOp{ {"success optional", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: bits.BindOptional, Flags: comp.BindOptional,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -46,7 +46,7 @@ func TestBindMountOp(t *testing.T) {
{"ensureFile device", new(Params), &BindMountOp{ {"ensureFile device", new(Params), &BindMountOp{
Source: check.MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: check.MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -57,7 +57,7 @@ func TestBindMountOp(t *testing.T) {
{"mkdirAll ensure", new(Params), &BindMountOp{ {"mkdirAll ensure", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/bin/"), Target: check.MustAbs("/bin/"),
Flags: bits.BindEnsure, Flags: comp.BindEnsure,
}, []stub.Call{ }, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)), call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, stub.UniqueError(4)),
}, stub.UniqueError(4), nil, nil}, }, stub.UniqueError(4), nil, nil},
@@ -65,7 +65,7 @@ func TestBindMountOp(t *testing.T) {
{"success ensure", new(Params), &BindMountOp{ {"success ensure", new(Params), &BindMountOp{
Source: check.MustAbs("/bin/"), Source: check.MustAbs("/bin/"),
Target: check.MustAbs("/usr/bin/"), Target: check.MustAbs("/usr/bin/"),
Flags: bits.BindEnsure, Flags: comp.BindEnsure,
}, []stub.Call{ }, []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil), call("mkdirAll", stub.ExpectArgs{"/bin/", os.FileMode(0700)}, nil, nil),
call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil), call("evalSymlinks", stub.ExpectArgs{"/bin/"}, "/usr/bin", nil),
@@ -79,7 +79,7 @@ func TestBindMountOp(t *testing.T) {
{"success device ro", new(Params), &BindMountOp{ {"success device ro", new(Params), &BindMountOp{
Source: check.MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: check.MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: bits.BindDevice, Flags: comp.BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -92,7 +92,7 @@ func TestBindMountOp(t *testing.T) {
{"success device", new(Params), &BindMountOp{ {"success device", new(Params), &BindMountOp{
Source: check.MustAbs("/dev/null"), Source: check.MustAbs("/dev/null"),
Target: check.MustAbs("/dev/null"), Target: check.MustAbs("/dev/null"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, []stub.Call{ }, []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil), call("evalSymlinks", stub.ExpectArgs{"/dev/null"}, "/dev/null", nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
@@ -182,7 +182,7 @@ func TestBindMountOp(t *testing.T) {
{"zero", new(BindMountOp), false}, {"zero", new(BindMountOp), false},
{"nil source", &BindMountOp{Target: check.MustAbs("/")}, false}, {"nil source", &BindMountOp{Target: check.MustAbs("/")}, false},
{"nil target", &BindMountOp{Source: check.MustAbs("/")}, false}, {"nil target", &BindMountOp{Source: check.MustAbs("/")}, false},
{"flag optional ensure", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/"), Flags: bits.BindOptional | bits.BindEnsure}, false}, {"flag optional ensure", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/"), Flags: comp.BindOptional | comp.BindEnsure}, false},
{"valid", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/")}, true}, {"valid", &BindMountOp{Source: check.MustAbs("/"), Target: check.MustAbs("/")}, true},
}) })
@@ -217,7 +217,7 @@ func TestBindMountOp(t *testing.T) {
}, &BindMountOp{ }, &BindMountOp{
Source: check.MustAbs("/etc/"), Source: check.MustAbs("/etc/"),
Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"), Target: check.MustAbs("/etc/.host/048090b6ed8f9ebb10e275ff5d8c0659"),
Flags: bits.BindOptional, Flags: comp.BindOptional,
}, false}, }, false},
{"source differs", &BindMountOp{ {"source differs", &BindMountOp{
@@ -256,7 +256,7 @@ func TestBindMountOp(t *testing.T) {
{"hostdev", &BindMountOp{ {"hostdev", &BindMountOp{
Source: check.MustAbs("/dev/"), Source: check.MustAbs("/dev/"),
Target: check.MustAbs("/dev/"), Target: check.MustAbs("/dev/"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}, "mounting", `"/dev/" flags 0x6`}, }, "mounting", `"/dev/" flags 0x6`},
}) })
} }

View File

@@ -1,7 +1,7 @@
package seccomp_test package seccomp_test
import ( import (
. "hakurei.app/container/bits" . "hakurei.app/container/comp"
. "hakurei.app/container/seccomp" . "hakurei.app/container/seccomp"
) )

View File

@@ -1,7 +1,7 @@
package seccomp_test package seccomp_test
import ( import (
. "hakurei.app/container/bits" . "hakurei.app/container/comp"
. "hakurei.app/container/seccomp" . "hakurei.app/container/seccomp"
) )

View File

@@ -4,14 +4,14 @@ import (
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
) )
type ( type (
bpfPreset = struct { bpfPreset = struct {
seccomp.ExportFlag seccomp.ExportFlag
bits.FilterPreset comp.FilterPreset
} }
bpfLookup map[bpfPreset][sha512.Size]byte bpfLookup map[bpfPreset][sha512.Size]byte
) )

View File

@@ -6,7 +6,7 @@ import (
"syscall" "syscall"
"testing" "testing"
. "hakurei.app/container/bits" . "hakurei.app/container/comp"
. "hakurei.app/container/seccomp" . "hakurei.app/container/seccomp"
) )

View File

@@ -5,32 +5,32 @@ package seccomp
import ( import (
. "syscall" . "syscall"
"hakurei.app/container/bits" "hakurei.app/container/comp"
) )
func Preset(presets bits.FilterPreset, flags ExportFlag) (rules []NativeRule) { func Preset(presets comp.FilterPreset, flags ExportFlag) (rules []NativeRule) {
allowedPersonality := PersonaLinux allowedPersonality := PersonaLinux
if presets&bits.PresetLinux32 != 0 { if presets&comp.PresetLinux32 != 0 {
allowedPersonality = PersonaLinux32 allowedPersonality = PersonaLinux32
} }
presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality)) presetDevelFinal := presetDevel(ScmpDatum(allowedPersonality))
l := len(presetCommon) l := len(presetCommon)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
l += len(presetNamespace) l += len(presetNamespace)
} }
if presets&bits.PresetDenyTTY != 0 { if presets&comp.PresetDenyTTY != 0 {
l += len(presetTTY) l += len(presetTTY)
} }
if presets&bits.PresetDenyDevel != 0 { if presets&comp.PresetDenyDevel != 0 {
l += len(presetDevelFinal) l += len(presetDevelFinal)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {
l += len(presetEmu) l += len(presetEmu)
} }
if presets&bits.PresetExt != 0 { if presets&comp.PresetExt != 0 {
l += len(presetCommonExt) l += len(presetCommonExt)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
l += len(presetNamespaceExt) l += len(presetNamespaceExt)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {
@@ -40,21 +40,21 @@ func Preset(presets bits.FilterPreset, flags ExportFlag) (rules []NativeRule) {
rules = make([]NativeRule, 0, l) rules = make([]NativeRule, 0, l)
rules = append(rules, presetCommon...) rules = append(rules, presetCommon...)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
rules = append(rules, presetNamespace...) rules = append(rules, presetNamespace...)
} }
if presets&bits.PresetDenyTTY != 0 { if presets&comp.PresetDenyTTY != 0 {
rules = append(rules, presetTTY...) rules = append(rules, presetTTY...)
} }
if presets&bits.PresetDenyDevel != 0 { if presets&comp.PresetDenyDevel != 0 {
rules = append(rules, presetDevelFinal...) rules = append(rules, presetDevelFinal...)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {
rules = append(rules, presetEmu...) rules = append(rules, presetEmu...)
} }
if presets&bits.PresetExt != 0 { if presets&comp.PresetExt != 0 {
rules = append(rules, presetCommonExt...) rules = append(rules, presetCommonExt...)
if presets&bits.PresetDenyNS != 0 { if presets&comp.PresetDenyNS != 0 {
rules = append(rules, presetNamespaceExt...) rules = append(rules, presetNamespaceExt...)
} }
if flags&AllowMultiarch == 0 { if flags&AllowMultiarch == 0 {

View File

@@ -1,50 +1,41 @@
package container package container
import ( import (
"syscall" . "syscall"
"unsafe" "unsafe"
) )
// SetPtracer allows processes to ptrace(2) the calling process. // Prctl manipulates various aspects of the behavior of the calling thread or process.
func SetPtracer(pid uintptr) error { func Prctl(op, arg2, arg3 uintptr) error {
_, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PTRACER, pid, 0) r, _, errno := Syscall(SYS_PRCTL, op, arg2, arg3)
if errno == 0 { if r < 0 {
return nil return errno
} }
return errno return nil
} }
// SetPtracer allows processes to ptrace(2) the calling process.
func SetPtracer(pid uintptr) error { return Prctl(PR_SET_PTRACER, pid, 0) }
// linux/sched/coredump.h
const ( const (
SUID_DUMP_DISABLE = iota SUID_DUMP_DISABLE = iota
SUID_DUMP_USER SUID_DUMP_USER
) )
// SetDumpable sets the "dumpable" attribute of the calling process. // SetDumpable sets the "dumpable" attribute of the calling process.
func SetDumpable(dumpable uintptr) error { func SetDumpable(dumpable uintptr) error { return Prctl(PR_SET_DUMPABLE, dumpable, 0) }
// linux/sched/coredump.h
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_DUMPABLE, dumpable, 0); errno != 0 {
return errno
}
return nil
}
// SetNoNewPrivs sets the calling thread's no_new_privs attribute. // SetNoNewPrivs sets the calling thread's no_new_privs attribute.
func SetNoNewPrivs() error { func SetNoNewPrivs() error { return Prctl(PR_SET_NO_NEW_PRIVS, 1, 0) }
_, _, errno := syscall.Syscall(syscall.SYS_PRCTL, PR_SET_NO_NEW_PRIVS, 1, 0)
if errno == 0 {
return nil
}
return errno
}
// Isatty tests whether a file descriptor refers to a terminal. // Isatty tests whether a file descriptor refers to a terminal.
func Isatty(fd int) bool { func Isatty(fd int) bool {
var buf [8]byte var buf [8]byte
r, _, _ := syscall.Syscall( r, _, _ := Syscall(
syscall.SYS_IOCTL, SYS_IOCTL,
uintptr(fd), uintptr(fd),
syscall.TIOCGWINSZ, TIOCGWINSZ,
uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&buf[0])),
) )
return r == 0 return r == 0
@@ -60,7 +51,7 @@ func Isatty(fd int) bool {
func IgnoringEINTR(fn func() error) error { func IgnoringEINTR(fn func() error) error {
for { for {
err := fn() err := fn()
if err != syscall.EINTR { if err != EINTR {
return err return err
} }
} }

8
dist/release.sh vendored
View File

@@ -10,10 +10,10 @@ cp -rv "dist/comp" "${out}"
go generate ./... go generate ./...
go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static' go build -trimpath -v -o "${out}/bin/" -ldflags "-s -w -buildid= -extldflags '-static'
-X hakurei.app/internal.version=${VERSION} -X hakurei.app/internal.buildVersion=${VERSION}
-X hakurei.app/internal.hmain=/usr/bin/hakurei -X hakurei.app/internal.hakureiPath=/usr/bin/hakurei
-X hakurei.app/internal.hsu=/usr/bin/hsu -X hakurei.app/internal.hsuPath=/usr/bin/hsu
-X main.hmain=/usr/bin/hakurei" ./... -X main.hakureiPath=/usr/bin/hakurei" ./...
rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}" rm -f "./${out}.tar.gz" && tar -C dist -czf "${out}.tar.gz" "${pname}"
rm -rf "./${out}" rm -rf "./${out}"

View File

@@ -2,6 +2,7 @@ package hst
import ( import (
"encoding/json" "encoding/json"
"strings"
"syscall" "syscall"
"time" "time"
@@ -38,9 +39,12 @@ const (
ExitRequest = 254 ExitRequest = 254
) )
// Flags are options held by [ContainerConfig].
type Flags uintptr
const ( const (
// FMultiarch unblocks syscalls required for multiarch to work on applicable targets. // FMultiarch unblocks syscalls required for multiarch to work on applicable targets.
FMultiarch uintptr = 1 << iota FMultiarch Flags = 1 << iota
// FSeccompCompat changes emitted seccomp filter programs to be identical to that of Flatpak. // FSeccompCompat changes emitted seccomp filter programs to be identical to that of Flatpak.
FSeccompCompat FSeccompCompat
@@ -74,6 +78,45 @@ const (
FAll = fMax - 1 FAll = fMax - 1
) )
func (flags Flags) String() string {
switch flags {
case FMultiarch:
return "multiarch"
case FSeccompCompat:
return "compat"
case FDevel:
return "devel"
case FUserns:
return "userns"
case FHostNet:
return "net"
case FHostAbstract:
return "abstract"
case FTty:
return "tty"
case FMapRealUID:
return "mapuid"
case FDevice:
return "device"
case FShareRuntime:
return "runtime"
case FShareTmpdir:
return "tmpdir"
default:
s := make([]string, 0, 1<<4)
for f := Flags(1); f < fMax; f <<= 1 {
if flags&f != 0 {
s = append(s, f.String())
}
}
if len(s) == 0 {
return "none"
}
return strings.Join(s, ", ")
}
}
// ContainerConfig describes the container configuration to be applied to an underlying [container]. // ContainerConfig describes the container configuration to be applied to an underlying [container].
type ContainerConfig struct { type ContainerConfig struct {
// Container UTS namespace hostname. // Container UTS namespace hostname.
@@ -106,7 +149,7 @@ type ContainerConfig struct {
Args []string `json:"args"` Args []string `json:"args"`
// Flags holds boolean options of [ContainerConfig]. // Flags holds boolean options of [ContainerConfig].
Flags uintptr `json:"-"` Flags Flags `json:"-"`
} }
// ContainerConfigF is [ContainerConfig] stripped of its methods. // ContainerConfigF is [ContainerConfig] stripped of its methods.

View File

@@ -3,6 +3,7 @@ package hst_test
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"math"
"reflect" "reflect"
"syscall" "syscall"
"testing" "testing"
@@ -10,6 +11,30 @@ import (
"hakurei.app/hst" "hakurei.app/hst"
) )
func TestFlagsString(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
flags hst.Flags
want string
}{
{"none", 0, "none"},
{"none high", hst.FAll + 1, "none"},
{"all", hst.FAll, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir"},
{"all high", math.MaxUint, "multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := tc.flags.String(); got != tc.want {
t.Errorf("String(%#b): %q, want %q", tc.flags, got, tc.want)
}
})
}
}
func TestContainerConfig(t *testing.T) { func TestContainerConfig(t *testing.T) {
t.Parallel() t.Parallel()

View File

@@ -4,8 +4,8 @@ import (
"encoding/gob" "encoding/gob"
"strings" "strings"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
) )
@@ -97,16 +97,16 @@ func (b *FSBind) Apply(z *ApplyState) {
} }
var flags int var flags int
if b.Write { if b.Write {
flags |= bits.BindWritable flags |= comp.BindWritable
} }
if b.Device { if b.Device {
flags |= bits.BindDevice | bits.BindWritable flags |= comp.BindDevice | comp.BindWritable
} }
if b.Ensure { if b.Ensure {
flags |= bits.BindEnsure flags |= comp.BindEnsure
} }
if b.Optional { if b.Optional {
flags |= bits.BindOptional flags |= comp.BindOptional
} }
switch { switch {

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/hst" "hakurei.app/hst"
) )
@@ -24,7 +24,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/dev"), Source: m("/mnt/dev"),
Target: m("/dev"), Target: m("/dev"),
Flags: bits.BindWritable | bits.BindDevice | bits.BindOptional, Flags: comp.BindWritable | comp.BindDevice | comp.BindOptional,
}}, m("/dev"), ms("/mnt/dev"), }}, m("/dev"), ms("/mnt/dev"),
"d+/mnt/dev:/dev"}, "d+/mnt/dev:/dev"},
@@ -36,7 +36,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/dev"), Source: m("/mnt/dev"),
Target: m("/dev"), Target: m("/dev"),
Flags: bits.BindWritable | bits.BindDevice | bits.BindEnsure, Flags: comp.BindWritable | comp.BindDevice | comp.BindEnsure,
}}, m("/dev"), ms("/mnt/dev"), }}, m("/dev"), ms("/mnt/dev"),
"d-/mnt/dev:/dev"}, "d-/mnt/dev:/dev"},
@@ -48,7 +48,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/dev"), Source: m("/mnt/dev"),
Target: m("/dev"), Target: m("/dev"),
Flags: bits.BindWritable | bits.BindDevice, Flags: comp.BindWritable | comp.BindDevice,
}}, m("/dev"), ms("/mnt/dev"), }}, m("/dev"), ms("/mnt/dev"),
"d*/mnt/dev:/dev"}, "d*/mnt/dev:/dev"},
@@ -59,7 +59,7 @@ func TestFSBind(t *testing.T) {
}, true, container.Ops{&container.BindMountOp{ }, true, container.Ops{&container.BindMountOp{
Source: m("/mnt/tmp"), Source: m("/mnt/tmp"),
Target: m("/tmp"), Target: m("/tmp"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}}, m("/tmp"), ms("/mnt/tmp"), }}, m("/tmp"), ms("/mnt/tmp"),
"w*/mnt/tmp:/tmp"}, "w*/mnt/tmp:/tmp"},
@@ -98,7 +98,7 @@ func TestFSBind(t *testing.T) {
Special: true, Special: true,
}, true, container.Ops{&container.AutoRootOp{ }, true, container.Ops{&container.AutoRootOp{
Host: m("/"), Host: m("/"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}}, m("/"), ms("/"), "autoroot:w"}, }}, m("/"), ms("/"), "autoroot:w"},
{"autoroot silly", &hst.FSBind{ {"autoroot silly", &hst.FSBind{
@@ -108,7 +108,7 @@ func TestFSBind(t *testing.T) {
Special: true, Special: true,
}, true, container.Ops{&container.AutoRootOp{ }, true, container.Ops{&container.AutoRootOp{
Host: m("/etc"), Host: m("/etc"),
Flags: bits.BindWritable, Flags: comp.BindWritable,
}}, m("/"), ms("/etc"), "autoroot:w:/etc"}, }}, m("/"), ms("/etc"), "autoroot:w:/etc"},
{"autoetc", &hst.FSBind{ {"autoetc", &hst.FSBind{

View File

@@ -52,7 +52,10 @@ type Paths struct {
RunDirPath *check.Absolute `json:"run_dir_path"` RunDirPath *check.Absolute `json:"run_dir_path"`
} }
// Info holds basic system information collected from the implementation.
type Info struct { type Info struct {
// Version is a hardcoded version string.
Version string `json:"version"`
// User is the userid according to hsu. // User is the userid according to hsu.
User int `json:"user"` User int `json:"user"`

View File

@@ -18,7 +18,7 @@ func Main(ctx context.Context, msg message.Msg, config *hst.Config) {
log.Fatal(err) log.Fatal(err)
} }
seal := outcome{syscallDispatcher: direct{}} seal := outcome{syscallDispatcher: direct{msg}}
if err := seal.finalise(ctx, msg, &id, config); err != nil { if err := seal.finalise(ctx, msg, &id, config); err != nil {
printMessageError("cannot seal app:", err) printMessageError("cannot seal app:", err)
os.Exit(1) os.Exit(1)

View File

@@ -17,9 +17,10 @@ import (
"time" "time"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state" "hakurei.app/internal/app/state"
"hakurei.app/message" "hakurei.app/message"
@@ -41,9 +42,267 @@ func TestApp(t *testing.T) {
wantSys *system.I wantSys *system.I
wantParams *container.Params wantParams *container.Params
}{ }{
{ {"template", new(stubNixOS), hst.Template(), checkExpectInstanceId, system.New(panicMsgContext{}, message.NewMsg(nil), 1000009).
"nixos permissive defaults no enablements", new(stubNixOS), // spParamsOp
&hst.Config{Container: &hst.ContainerConfig{ Ensure(m("/tmp/hakurei.0"), 0711).
// spRuntimeOp
Ensure(m("/tmp/hakurei.0/runtime"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute).
// spTmpdirOp
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read, acl.Write, acl.Execute).
// instance
Ephemeral(system.Process, m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0711).
// spWaylandOp
Wayland(
m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"),
m("/run/user/1971/wayland-0"),
"org.chromium.Chromium",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
).
// ensureRuntimeDir
Ensure(m("/run/user/1971/hakurei"), 0700).
UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
Ensure(m("/run/user/1971"), 0700).
UpdatePermType(system.User, m("/run/user/1971"), acl.Execute).
// runtime
Ephemeral(system.Process, m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0700).
UpdatePerm(m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), acl.Execute).
// spPulseOp
Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse")).
// spDBusOp
MustProxyDBus(
hst.Template().SessionBus,
hst.Template().SystemBus, dbus.ProxyPair{
"unix:path=/run/user/1971/bus",
"/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus",
}, dbus.ProxyPair{
"unix:path=/var/run/dbus/system_bus_socket",
"/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket",
},
).UpdatePerm(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), acl.Read, acl.Write).
// spFilesystemOp
Ensure(m("/var/lib/hakurei/u0"), 0700).
UpdatePermType(system.User, m("/var/lib/hakurei/u0"), acl.Execute).
UpdatePermType(system.User, m("/var/lib/hakurei/u0/org.chromium.Chromium"), acl.Read, acl.Write, acl.Execute), &container.Params{
Dir: m("/data/data/org.chromium.Chromium"),
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"GOOGLE_API_KEY=AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT",
"HOME=/data/data/org.chromium.Chromium",
"PULSE_COOKIE=/.hakurei/pulse-cookie",
"PULSE_SERVER=unix:/run/user/1971/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1971",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
// spParamsOp
Hostname: "localhost",
RetainSession: true,
HostNet: true,
HostAbstract: true,
Path: m("/run/current-system/sw/bin/chromium"),
Args: []string{
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
"--enable-features=UseOzonePlatform",
"--ozone-platform=wayland",
},
SeccompFlags: seccomp.AllowMultiarch,
Uid: 1971,
Gid: 100,
Ops: new(container.Ops).
// resolveRoot
Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
// spParamsOp
Proc(fhs.AbsProc).
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
// spRuntimeOp
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/1971"), comp.BindWritable).
// spTmpdirOp
Bind(m("/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, comp.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1971:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0).
// spPulseOp
Bind(m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse"), m("/run/user/1971/pulse/native"), 0).
Place(m("/.hakurei/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
// spDBusOp
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1971/bus"), 0).
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
// spFilesystemOp
Etc(fhs.AbsEtc, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").
Tmpfs(fhs.AbsTmp, 0, 0755).
Overlay(m("/nix/store"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/upper"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/work"),
fhs.AbsVarLib.Append("hakurei/base/org.nixos/ro-store")).
Link(m("/run/current-system"), "/run/current-system", true).
Link(m("/run/opengl-driver"), "/run/opengl-driver", true).
Bind(fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"),
m("/data/data/org.chromium.Chromium"),
comp.BindWritable|comp.BindEnsure).
Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"),
comp.BindOptional|comp.BindWritable|comp.BindDevice).
Remount(fhs.AbsRoot, syscall.MS_RDONLY),
}},
{"nixos permissive defaults no enablements", new(stubNixOS), &hst.Config{Container: &hst.ContainerConfig{
Filesystem: []hst.FilesystemConfigJSON{
{FilesystemConfig: &hst.FSBind{
Target: fhs.AbsRoot,
Source: fhs.AbsRoot,
Write: true,
Special: true,
}},
{FilesystemConfig: &hst.FSBind{
Source: fhs.AbsDev.Append("kvm"),
Device: true,
Optional: true,
}},
{FilesystemConfig: &hst.FSBind{
Target: fhs.AbsEtc,
Source: fhs.AbsEtc,
Special: true,
}},
},
Username: "chronos",
Shell: m("/run/current-system/sw/bin/zsh"),
Home: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"/run/current-system/sw/bin/zsh"},
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
}}, state.ID{
0x4a, 0x45, 0x0b, 0x65,
0x96, 0xd7, 0xbc, 0x15,
0xbd, 0x01, 0x78, 0x0e,
0xb9, 0xa6, 0x07, 0xac,
}, system.New(t.Context(), msg, 1000000).
Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/0"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/0"), acl.Read, acl.Write, acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/0"), 01700).
UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/0"), acl.Read, acl.Write, acl.Execute), &container.Params{
Dir: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"/run/current-system/sw/bin/zsh"},
Env: []string{
"HOME=/home/chronos",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"XDG_RUNTIME_DIR=/run/user/65534",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=tty",
},
Ops: new(container.Ops).
Root(m("/"), comp.BindWritable).
Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), comp.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), comp.BindWritable).
Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:65534:\n")).
Bind(m("/dev/kvm"), m("/dev/kvm"), comp.BindWritable|comp.BindDevice|comp.BindOptional).
Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac").
Tmpfs(m("/run/user/1971"), 8192, 0755).
Tmpfs(m("/run/nscd"), 8192, 0755).
Tmpfs(m("/run/dbus"), 8192, 0755).
Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: comp.PresetExt | comp.PresetDenyDevel,
HostNet: true,
HostAbstract: true,
RetainSession: true,
ForwardCancel: true,
}},
{"nixos permissive defaults chromium", new(stubNixOS), &hst.Config{
ID: "org.chromium.Chromium",
Identity: 9,
Groups: []string{"video"},
SessionBus: &hst.BusConfig{
Talk: []string{
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{
"org.freedesktop.portal.*": "*",
},
Broadcast: map[string]string{
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*",
},
Filter: true,
},
SystemBus: &hst.BusConfig{
Talk: []string{
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower",
},
Filter: true,
},
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
Container: &hst.ContainerConfig{
Filesystem: []hst.FilesystemConfigJSON{ Filesystem: []hst.FilesystemConfigJSON{
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
Target: fhs.AbsRoot, Target: fhs.AbsRoot,
@@ -51,6 +310,11 @@ func TestApp(t *testing.T) {
Write: true, Write: true,
Special: true, Special: true,
}}, }},
{FilesystemConfig: &hst.FSBind{
Source: fhs.AbsDev.Append("dri"),
Device: true,
Optional: true,
}},
{FilesystemConfig: &hst.FSBind{ {FilesystemConfig: &hst.FSBind{
Source: fhs.AbsDev.Append("kvm"), Source: fhs.AbsDev.Append("kvm"),
Device: true, Device: true,
@@ -68,395 +332,264 @@ func TestApp(t *testing.T) {
Home: m("/home/chronos"), Home: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"), Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"/run/current-system/sw/bin/zsh"}, Args: []string{"zsh", "-c", "exec chromium "},
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir, Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
}},
state.ID{
0x4a, 0x45, 0x0b, 0x65,
0x96, 0xd7, 0xbc, 0x15,
0xbd, 0x01, 0x78, 0x0e,
0xb9, 0xa6, 0x07, 0xac,
}, },
system.New(t.Context(), msg, 1000000). }, state.ID{
Ensure(m("/tmp/hakurei.0"), 0711). 0xeb, 0xf0, 0x83, 0xd1,
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute). 0xb1, 0x75, 0x91, 0x17,
Ensure(m("/tmp/hakurei.0/runtime/0"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/0"), acl.Read, acl.Write, acl.Execute). 0x82, 0xd4, 0x13, 0x36,
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute). 0x9b, 0x64, 0xce, 0x7c,
Ensure(m("/tmp/hakurei.0/tmpdir/0"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/0"), acl.Read, acl.Write, acl.Execute), }, system.New(t.Context(), msg, 1000009).
&container.Params{ Ensure(m("/tmp/hakurei.0"), 0711).
Dir: m("/home/chronos"), Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Path: m("/run/current-system/sw/bin/zsh"), Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute).
Args: []string{"/run/current-system/sw/bin/zsh"}, Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Env: []string{ Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read, acl.Write, acl.Execute).
"HOME=/home/chronos", Ephemeral(system.Process, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c"), 0711).
"SHELL=/run/current-system/sw/bin/zsh", Wayland(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c").
"TERM=xterm-256color", Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
"USER=chronos", Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
"XDG_RUNTIME_DIR=/run/user/65534", Ephemeral(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), acl.Execute).
"XDG_SESSION_CLASS=user", Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse")).
"XDG_SESSION_TYPE=tty", MustProxyDBus(&hst.BusConfig{
Talk: []string{
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager",
}, },
Ops: new(container.Ops). Own: []string{
Root(m("/"), bits.BindWritable). "org.chromium.Chromium.*",
Proc(m("/proc/")). "org.mpris.MediaPlayer2.org.chromium.Chromium.*",
Tmpfs(hst.AbsPrivateTmp, 4096, 0755). "org.mpris.MediaPlayer2.chromium.*",
DevWritable(m("/dev/"), true). },
Tmpfs(m("/dev/shm"), 0, 01777). Call: map[string]string{
Tmpfs(m("/run/user/"), 4096, 0755). "org.freedesktop.portal.*": "*",
Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), bits.BindWritable). },
Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), bits.BindWritable). Broadcast: map[string]string{
Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). "org.freedesktop.portal.*": "@/org/freedesktop/portal/*",
Place(m("/etc/group"), []byte("hakurei:x:65534:\n")). },
Bind(m("/dev/kvm"), m("/dev/kvm"), bits.BindWritable|bits.BindDevice|bits.BindOptional). Filter: true,
Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac"). }, &hst.BusConfig{
Tmpfs(m("/run/user/1971"), 8192, 0755). Talk: []string{
Tmpfs(m("/run/nscd"), 8192, 0755). "org.bluez",
Tmpfs(m("/run/dbus"), 8192, 0755). "org.freedesktop.Avahi",
Remount(m("/dev/"), syscall.MS_RDONLY). "org.freedesktop.UPower",
Remount(m("/"), syscall.MS_RDONLY), },
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel, Filter: true,
HostNet: true, }, dbus.ProxyPair{
HostAbstract: true, "unix:path=/run/user/1971/bus",
RetainSession: true, "/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus",
ForwardCancel: true, }, dbus.ProxyPair{
"unix:path=/var/run/dbus/system_bus_socket",
"/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket",
}).
UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), acl.Read, acl.Write), &container.Params{
Dir: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"zsh", "-c", "exec chromium "},
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"HOME=/home/chronos",
"PULSE_COOKIE=" + hst.PrivateTmp + "/pulse-cookie",
"PULSE_SERVER=unix:/run/user/65534/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/65534",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
}, },
}, Ops: new(container.Ops).
{ Root(m("/"), comp.BindWritable).
"nixos permissive defaults chromium", new(stubNixOS), Proc(m("/proc/")).
&hst.Config{ Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
ID: "org.chromium.Chromium", DevWritable(m("/dev/"), true).
Identity: 9, Tmpfs(m("/dev/shm"), 0, 01777).
Groups: []string{"video"}, Tmpfs(m("/run/user/"), 4096, 0755).
SessionBus: &hst.BusConfig{ Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), comp.BindWritable).
Talk: []string{ Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), comp.BindWritable).
"org.freedesktop.Notifications", Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
"org.freedesktop.FileManager1", Place(m("/etc/group"), []byte("hakurei:x:65534:\n")).
"org.freedesktop.ScreenSaver", Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/65534/wayland-0"), 0).
"org.freedesktop.secrets", Bind(m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse"), m("/run/user/65534/pulse/native"), 0).
"org.kde.kwalletd5", Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
"org.kde.kwalletd6", Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), m("/run/user/65534/bus"), 0).
"org.gnome.SessionManager", Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
}, Bind(m("/dev/dri"), m("/dev/dri"), comp.BindWritable|comp.BindDevice|comp.BindOptional).
Own: []string{ Bind(m("/dev/kvm"), m("/dev/kvm"), comp.BindWritable|comp.BindDevice|comp.BindOptional).
"org.chromium.Chromium.*", Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c").
"org.mpris.MediaPlayer2.org.chromium.Chromium.*", Tmpfs(m("/run/user/1971"), 8192, 0755).
"org.mpris.MediaPlayer2.chromium.*", Tmpfs(m("/run/nscd"), 8192, 0755).
}, Tmpfs(m("/run/dbus"), 8192, 0755).
Call: map[string]string{ Remount(m("/dev/"), syscall.MS_RDONLY).
"org.freedesktop.portal.*": "*", Remount(m("/"), syscall.MS_RDONLY),
}, SeccompPresets: comp.PresetExt | comp.PresetDenyDevel,
Broadcast: map[string]string{ HostNet: true,
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*", HostAbstract: true,
}, RetainSession: true,
Filter: true, ForwardCancel: true,
}},
{"nixos chromium direct wayland", new(stubNixOS), &hst.Config{
ID: "org.chromium.Chromium",
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
Container: &hst.ContainerConfig{
Env: nil,
Filesystem: []hst.FilesystemConfigJSON{
f(&hst.FSBind{Source: m("/bin")}),
f(&hst.FSBind{Source: m("/usr/bin/")}),
f(&hst.FSBind{Source: m("/nix/store")}),
f(&hst.FSBind{Source: m("/run/current-system")}),
f(&hst.FSBind{Source: m("/sys/block"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/bus"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/class"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/dev"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/devices"), Optional: true}),
f(&hst.FSBind{Source: m("/run/opengl-driver")}),
f(&hst.FSBind{Source: m("/dev/dri"), Device: true, Optional: true}),
f(&hst.FSBind{Source: m("/etc/"), Target: m("/etc/"), Special: true}),
f(&hst.FSBind{Source: m("/var/lib/persist/module/hakurei/0/1"), Write: true, Ensure: true}),
}, },
SystemBus: &hst.BusConfig{
Talk: []string{
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower",
},
Filter: true,
},
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
Container: &hst.ContainerConfig{ Username: "u0_a1",
Filesystem: []hst.FilesystemConfigJSON{ Shell: m("/run/current-system/sw/bin/zsh"),
{FilesystemConfig: &hst.FSBind{ Home: m("/var/lib/persist/module/hakurei/0/1"),
Target: fhs.AbsRoot,
Source: fhs.AbsRoot,
Write: true,
Special: true,
}},
{FilesystemConfig: &hst.FSBind{
Source: fhs.AbsDev.Append("dri"),
Device: true,
Optional: true,
}},
{FilesystemConfig: &hst.FSBind{
Source: fhs.AbsDev.Append("kvm"),
Device: true,
Optional: true,
}},
{FilesystemConfig: &hst.FSBind{
Target: fhs.AbsEtc,
Source: fhs.AbsEtc,
Special: true,
}},
},
Username: "chronos",
Shell: m("/run/current-system/sw/bin/zsh"),
Home: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"zsh", "-c", "exec chromium "},
Flags: hst.FUserns | hst.FHostNet | hst.FHostAbstract | hst.FTty | hst.FShareRuntime | hst.FShareTmpdir,
},
},
state.ID{
0xeb, 0xf0, 0x83, 0xd1,
0xb1, 0x75, 0x91, 0x17,
0x82, 0xd4, 0x13, 0x36,
0x9b, 0x64, 0xce, 0x7c,
},
system.New(t.Context(), msg, 1000009).
Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/9"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/9"), acl.Read, acl.Write, acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read, acl.Write, acl.Execute).
Ephemeral(system.Process, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c"), 0711).
Wayland(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c").
Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
Ephemeral(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), acl.Execute).
Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse")).
MustProxyDBus(&hst.BusConfig{
Talk: []string{
"org.freedesktop.Notifications",
"org.freedesktop.FileManager1",
"org.freedesktop.ScreenSaver",
"org.freedesktop.secrets",
"org.kde.kwalletd5",
"org.kde.kwalletd6",
"org.gnome.SessionManager",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{
"org.freedesktop.portal.*": "*",
},
Broadcast: map[string]string{
"org.freedesktop.portal.*": "@/org/freedesktop/portal/*",
},
Filter: true,
}, &hst.BusConfig{
Talk: []string{
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower",
},
Filter: true,
}, dbus.ProxyPair{
"unix:path=/run/user/1971/bus",
"/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus",
}, dbus.ProxyPair{
"unix:path=/var/run/dbus/system_bus_socket",
"/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket",
}).
UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), acl.Read, acl.Write),
&container.Params{
Dir: m("/home/chronos"),
Path: m("/run/current-system/sw/bin/zsh"),
Args: []string{"zsh", "-c", "exec chromium "},
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"HOME=/home/chronos",
"PULSE_COOKIE=" + hst.PrivateTmp + "/pulse-cookie",
"PULSE_SERVER=unix:/run/user/65534/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/65534",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
Ops: new(container.Ops).
Root(m("/"), bits.BindWritable).
Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), bits.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), bits.BindWritable).
Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:65534:\n")).
Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/65534/wayland-0"), 0).
Bind(m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse"), m("/run/user/65534/pulse/native"), 0).
Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), m("/run/user/65534/bus"), 0).
Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
Bind(m("/dev/dri"), m("/dev/dri"), bits.BindWritable|bits.BindDevice|bits.BindOptional).
Bind(m("/dev/kvm"), m("/dev/kvm"), bits.BindWritable|bits.BindDevice|bits.BindOptional).
Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c").
Tmpfs(m("/run/user/1971"), 8192, 0755).
Tmpfs(m("/run/nscd"), 8192, 0755).
Tmpfs(m("/run/dbus"), 8192, 0755).
Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel,
HostNet: true,
HostAbstract: true,
RetainSession: true,
ForwardCancel: true,
},
},
{
"nixos chromium direct wayland", new(stubNixOS),
&hst.Config{
ID: "org.chromium.Chromium",
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
Container: &hst.ContainerConfig{
Env: nil,
Filesystem: []hst.FilesystemConfigJSON{
f(&hst.FSBind{Source: m("/bin")}),
f(&hst.FSBind{Source: m("/usr/bin/")}),
f(&hst.FSBind{Source: m("/nix/store")}),
f(&hst.FSBind{Source: m("/run/current-system")}),
f(&hst.FSBind{Source: m("/sys/block"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/bus"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/class"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/dev"), Optional: true}),
f(&hst.FSBind{Source: m("/sys/devices"), Optional: true}),
f(&hst.FSBind{Source: m("/run/opengl-driver")}),
f(&hst.FSBind{Source: m("/dev/dri"), Device: true, Optional: true}),
f(&hst.FSBind{Source: m("/etc/"), Target: m("/etc/"), Special: true}),
f(&hst.FSBind{Source: m("/var/lib/persist/module/hakurei/0/1"), Write: true, Ensure: true}),
},
Username: "u0_a1",
Shell: m("/run/current-system/sw/bin/zsh"),
Home: m("/var/lib/persist/module/hakurei/0/1"),
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FShareRuntime | hst.FShareTmpdir,
},
SystemBus: &hst.BusConfig{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
Filter: true,
},
SessionBus: &hst.BusConfig{
Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.kde.kwalletd5", "org.kde.kwalletd6",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true,
},
DirectWayland: true,
Identity: 1, Groups: []string{},
},
state.ID{
0x8e, 0x2c, 0x76, 0xb0,
0x66, 0xda, 0xbe, 0x57,
0x4c, 0xf0, 0x73, 0xbd,
0xb4, 0x6e, 0xb5, 0xc1,
},
system.New(t.Context(), msg, 1000001).
Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/1"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/1"), acl.Read, acl.Write, acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/1"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/1"), acl.Read, acl.Write, acl.Execute).
Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read, acl.Write, acl.Execute).
Ephemeral(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), acl.Execute).
Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse")).
Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711).
MustProxyDBus(&hst.BusConfig{
Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.kde.kwalletd5", "org.kde.kwalletd6",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true,
}, &hst.BusConfig{
Talk: []string{
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower",
},
Filter: true,
}, dbus.ProxyPair{
"unix:path=/run/user/1971/bus",
"/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus",
}, dbus.ProxyPair{
"unix:path=/var/run/dbus/system_bus_socket",
"/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket",
}).
UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), acl.Read, acl.Write),
&container.Params{
Uid: 1971,
Gid: 100,
Dir: m("/var/lib/persist/module/hakurei/0/1"),
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
Env: []string{ Flags: hst.FUserns | hst.FHostNet | hst.FMapRealUID | hst.FShareRuntime | hst.FShareTmpdir,
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"HOME=/var/lib/persist/module/hakurei/0/1",
"PULSE_COOKIE=" + hst.PrivateTmp + "/pulse-cookie",
"PULSE_SERVER=unix:/run/user/1971/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=u0_a1",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1971",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
Ops: new(container.Ops).
Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), bits.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), bits.BindWritable).
Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0).
Bind(m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse"), m("/run/user/1971/pulse/native"), 0).
Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
Bind(m("/bin"), m("/bin"), 0).
Bind(m("/usr/bin/"), m("/usr/bin/"), 0).
Bind(m("/nix/store"), m("/nix/store"), 0).
Bind(m("/run/current-system"), m("/run/current-system"), 0).
Bind(m("/sys/block"), m("/sys/block"), bits.BindOptional).
Bind(m("/sys/bus"), m("/sys/bus"), bits.BindOptional).
Bind(m("/sys/class"), m("/sys/class"), bits.BindOptional).
Bind(m("/sys/dev"), m("/sys/dev"), bits.BindOptional).
Bind(m("/sys/devices"), m("/sys/devices"), bits.BindOptional).
Bind(m("/run/opengl-driver"), m("/run/opengl-driver"), 0).
Bind(m("/dev/dri"), m("/dev/dri"), bits.BindDevice|bits.BindWritable|bits.BindOptional).
Etc(m("/etc/"), "8e2c76b066dabe574cf073bdb46eb5c1").
Bind(m("/var/lib/persist/module/hakurei/0/1"), m("/var/lib/persist/module/hakurei/0/1"), bits.BindWritable|bits.BindEnsure).
Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: bits.PresetExt | bits.PresetDenyTTY | bits.PresetDenyDevel,
HostNet: true,
ForwardCancel: true,
}, },
}, SystemBus: &hst.BusConfig{
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
Filter: true,
},
SessionBus: &hst.BusConfig{
Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.kde.kwalletd5", "org.kde.kwalletd6",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true,
},
DirectWayland: true,
Identity: 1, Groups: []string{},
}, state.ID{
0x8e, 0x2c, 0x76, 0xb0,
0x66, 0xda, 0xbe, 0x57,
0x4c, 0xf0, 0x73, 0xbd,
0xb4, 0x6e, 0xb5, 0xc1,
}, system.New(t.Context(), msg, 1000001).
Ensure(m("/tmp/hakurei.0"), 0711).
Ensure(m("/tmp/hakurei.0/runtime"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime"), acl.Execute).
Ensure(m("/tmp/hakurei.0/runtime/1"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/runtime/1"), acl.Read, acl.Write, acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir"), 0700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir"), acl.Execute).
Ensure(m("/tmp/hakurei.0/tmpdir/1"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/1"), acl.Read, acl.Write, acl.Execute).
Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read, acl.Write, acl.Execute).
Ephemeral(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), acl.Execute).
Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse")).
Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711).
MustProxyDBus(&hst.BusConfig{
Talk: []string{
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
"org.freedesktop.ScreenSaver", "org.freedesktop.secrets",
"org.kde.kwalletd5", "org.kde.kwalletd6",
},
Own: []string{
"org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
"org.mpris.MediaPlayer2.chromium.*",
},
Call: map[string]string{}, Broadcast: map[string]string{},
Filter: true,
}, &hst.BusConfig{
Talk: []string{
"org.bluez",
"org.freedesktop.Avahi",
"org.freedesktop.UPower",
},
Filter: true,
}, dbus.ProxyPair{
"unix:path=/run/user/1971/bus",
"/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus",
}, dbus.ProxyPair{
"unix:path=/var/run/dbus/system_bus_socket",
"/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket",
}).
UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), acl.Read, acl.Write).
UpdatePerm(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), acl.Read, acl.Write), &container.Params{
Uid: 1971,
Gid: 100,
Dir: m("/var/lib/persist/module/hakurei/0/1"),
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
Args: []string{"/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"},
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"HOME=/var/lib/persist/module/hakurei/0/1",
"PULSE_COOKIE=" + hst.PrivateTmp + "/pulse-cookie",
"PULSE_SERVER=unix:/run/user/1971/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=u0_a1",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1971",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
Ops: new(container.Ops).
Proc(m("/proc/")).
Tmpfs(hst.AbsPrivateTmp, 4096, 0755).
DevWritable(m("/dev/"), true).
Tmpfs(m("/dev/shm"), 0, 01777).
Tmpfs(m("/run/user/"), 4096, 0755).
Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), comp.BindWritable).
Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), comp.BindWritable).
Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0).
Bind(m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse"), m("/run/user/1971/pulse/native"), 0).
Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0).
Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
Bind(m("/bin"), m("/bin"), 0).
Bind(m("/usr/bin/"), m("/usr/bin/"), 0).
Bind(m("/nix/store"), m("/nix/store"), 0).
Bind(m("/run/current-system"), m("/run/current-system"), 0).
Bind(m("/sys/block"), m("/sys/block"), comp.BindOptional).
Bind(m("/sys/bus"), m("/sys/bus"), comp.BindOptional).
Bind(m("/sys/class"), m("/sys/class"), comp.BindOptional).
Bind(m("/sys/dev"), m("/sys/dev"), comp.BindOptional).
Bind(m("/sys/devices"), m("/sys/devices"), comp.BindOptional).
Bind(m("/run/opengl-driver"), m("/run/opengl-driver"), 0).
Bind(m("/dev/dri"), m("/dev/dri"), comp.BindDevice|comp.BindWritable|comp.BindOptional).
Etc(m("/etc/"), "8e2c76b066dabe574cf073bdb46eb5c1").
Bind(m("/var/lib/persist/module/hakurei/0/1"), m("/var/lib/persist/module/hakurei/0/1"), comp.BindWritable|comp.BindEnsure).
Remount(m("/dev/"), syscall.MS_RDONLY).
Remount(m("/"), syscall.MS_RDONLY),
SeccompPresets: comp.PresetExt | comp.PresetDenyTTY | comp.PresetDenyDevel,
HostNet: true,
ForwardCancel: true,
}},
} }
for _, tc := range testCases { for _, tc := range testCases {
@@ -574,10 +707,9 @@ func (s stubOsFileReadCloser) Stat() (fs.FileInfo, error) { panic("attempting to
type stubNixOS struct { type stubNixOS struct {
usernameErr map[string]error usernameErr map[string]error
panicDispatcher
} }
func (k *stubNixOS) new(func(k syscallDispatcher)) { panic("not implemented") }
func (k *stubNixOS) getpid() int { return 0xdeadbeef } func (k *stubNixOS) getpid() int { return 0xdeadbeef }
func (k *stubNixOS) getuid() int { return 1971 } func (k *stubNixOS) getuid() int { return 1971 }
func (k *stubNixOS) getgid() int { return 100 } func (k *stubNixOS) getgid() int { return 100 }
@@ -659,6 +791,10 @@ func (k *stubNixOS) readdir(name string) ([]fs.DirEntry, error) {
"tmpfiles.d", "udev", "udisks2", "UPower", "vconsole.conf", "X11", "zfs", "zinputrc", "tmpfiles.d", "udev", "udisks2", "UPower", "vconsole.conf", "X11", "zfs", "zinputrc",
"zoneinfo", "zprofile", "zshenv", "zshrc") "zoneinfo", "zprofile", "zshenv", "zshrc")
case "/var/lib/hakurei/base/org.debian":
return stubDirEntries("bin", "dev", "etc", "home", "lib64", "lost+found",
"mnt", "nix", "proc", "root", "run", "srv", "sys", "tmp", "usr", "var")
default: default:
panic(fmt.Sprintf("attempted to read unexpected directory %q", name)) panic(fmt.Sprintf("attempted to read unexpected directory %q", name))
} }
@@ -726,6 +862,38 @@ func (k *stubNixOS) evalSymlinks(path string) (string, error) {
return "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-graphics-drivers", nil return "/nix/store/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-graphics-drivers", nil
case "/var/lib/persist/module/hakurei/0/1": case "/var/lib/persist/module/hakurei/0/1":
return "/var/lib/persist/module/hakurei/0/1", nil return "/var/lib/persist/module/hakurei/0/1", nil
case "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper":
return "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper", nil
case "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work":
return "/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work", nil
case "/var/lib/hakurei/base/org.nixos/ro-store":
return "/var/lib/hakurei/base/org.nixos/ro-store", nil
case "/var/lib/hakurei/u0/org.chromium.Chromium":
return "/var/lib/hakurei/u0/org.chromium.Chromium", nil
case "/var/lib/hakurei/base/org.debian/bin":
return "/var/lib/hakurei/base/org.debian/bin", nil
case "/var/lib/hakurei/base/org.debian/home":
return "/var/lib/hakurei/base/org.debian/home", nil
case "/var/lib/hakurei/base/org.debian/lib64":
return "/var/lib/hakurei/base/org.debian/lib64", nil
case "/var/lib/hakurei/base/org.debian/lost+found":
return "/var/lib/hakurei/base/org.debian/lost+found", nil
case "/var/lib/hakurei/base/org.debian/nix":
return "/var/lib/hakurei/base/org.debian/nix", nil
case "/var/lib/hakurei/base/org.debian/root":
return "/var/lib/hakurei/base/org.debian/root", nil
case "/var/lib/hakurei/base/org.debian/run":
return "/var/lib/hakurei/base/org.debian/run", nil
case "/var/lib/hakurei/base/org.debian/srv":
return "/var/lib/hakurei/base/org.debian/srv", nil
case "/var/lib/hakurei/base/org.debian/sys":
return "/var/lib/hakurei/base/org.debian/sys", nil
case "/var/lib/hakurei/base/org.debian/usr":
return "/var/lib/hakurei/base/org.debian/usr", nil
case "/var/lib/hakurei/base/org.debian/var":
return "/var/lib/hakurei/base/org.debian/var", nil
default: default:
panic(fmt.Sprintf("attempted to evaluate unexpected path %q", path)) panic(fmt.Sprintf("attempted to evaluate unexpected path %q", path))
} }

View File

@@ -1,16 +1,18 @@
package app package app
import ( import (
"context"
"io" "io"
"io/fs" "io/fs"
"log"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"os/user" "os/user"
"path/filepath" "path/filepath"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/seccomp"
"hakurei.app/internal" "hakurei.app/internal"
"hakurei.app/message" "hakurei.app/message"
"hakurei.app/system/dbus" "hakurei.app/system/dbus"
@@ -28,7 +30,7 @@ type syscallDispatcher interface {
// new starts a goroutine with a new instance of syscallDispatcher. // new starts a goroutine with a new instance of syscallDispatcher.
// A syscallDispatcher must never be used in any goroutine other than the one owning it, // A syscallDispatcher must never be used in any goroutine other than the one owning it,
// just synchronising access is not enough, as this is for test instrumentation. // just synchronising access is not enough, as this is for test instrumentation.
new(f func(k syscallDispatcher)) new(f func(k syscallDispatcher, msg message.Msg))
// getpid provides [os.Getpid]. // getpid provides [os.Getpid].
getpid() int getpid() int
@@ -38,6 +40,8 @@ type syscallDispatcher interface {
getgid() int getgid() int
// lookupEnv provides [os.LookupEnv]. // lookupEnv provides [os.LookupEnv].
lookupEnv(key string) (string, bool) lookupEnv(key string) (string, bool)
// pipe provides os.Pipe.
pipe() (r, w *os.File, err error)
// stat provides [os.Stat]. // stat provides [os.Stat].
stat(name string) (os.FileInfo, error) stat(name string) (os.FileInfo, error)
// open provides [os.Open]. // open provides [os.Open].
@@ -46,6 +50,8 @@ type syscallDispatcher interface {
readdir(name string) ([]os.DirEntry, error) readdir(name string) ([]os.DirEntry, error)
// tempdir provides [os.TempDir]. // tempdir provides [os.TempDir].
tempdir() string tempdir() string
// exit provides [os.Exit].
exit(code int)
// evalSymlinks provides [filepath.EvalSymlinks]. // evalSymlinks provides [filepath.EvalSymlinks].
evalSymlinks(path string) (string, error) evalSymlinks(path string) (string, error)
@@ -56,10 +62,29 @@ type syscallDispatcher interface {
// cmdOutput provides the Output method of [exec.Cmd]. // cmdOutput provides the Output method of [exec.Cmd].
cmdOutput(cmd *exec.Cmd) ([]byte, error) cmdOutput(cmd *exec.Cmd) ([]byte, error)
// notifyContext provides [signal.NotifyContext].
notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc)
// prctl provides [container.Prctl].
prctl(op, arg2, arg3 uintptr) error
// overflowUid provides [container.OverflowUid]. // overflowUid provides [container.OverflowUid].
overflowUid(msg message.Msg) int overflowUid(msg message.Msg) int
// overflowGid provides [container.OverflowGid]. // overflowGid provides [container.OverflowGid].
overflowGid(msg message.Msg) int overflowGid(msg message.Msg) int
// setDumpable provides [container.SetDumpable].
setDumpable(dumpable uintptr) error
// receive provides [container.Receive].
receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error)
// containerStart provides the Start method of [container.Container].
containerStart(z *container.Container) error
// containerStart provides the Serve method of [container.Container].
containerServe(z *container.Container) error
// containerStart provides the Wait method of [container.Container].
containerWait(z *container.Container) error
// seccompLoad provides [seccomp.Load].
seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error
// mustHsuPath provides [internal.MustHsuPath]. // mustHsuPath provides [internal.MustHsuPath].
mustHsuPath() *check.Absolute mustHsuPath() *check.Absolute
@@ -67,23 +92,32 @@ type syscallDispatcher interface {
// dbusAddress provides [dbus.Address]. // dbusAddress provides [dbus.Address].
dbusAddress() (session, system string) dbusAddress() (session, system string)
// setupContSignal provides setupContSignal.
setupContSignal(pid int) (io.ReadCloser, func(), error)
// getMsg returns the [message.Msg] held by syscallDispatcher.
getMsg() message.Msg
// fatal provides [log.Fatal].
fatal(v ...any)
// fatalf provides [log.Fatalf]. // fatalf provides [log.Fatalf].
fatalf(format string, v ...any) fatalf(format string, v ...any)
} }
// direct implements syscallDispatcher on the current kernel. // direct implements syscallDispatcher on the current kernel.
type direct struct{} type direct struct{ msg message.Msg }
func (k direct) new(f func(k syscallDispatcher)) { go f(k) } func (k direct) new(f func(k syscallDispatcher, msg message.Msg)) { go f(k, k.msg) }
func (direct) getpid() int { return os.Getpid() } func (direct) getpid() int { return os.Getpid() }
func (direct) getuid() int { return os.Getuid() } func (direct) getuid() int { return os.Getuid() }
func (direct) getgid() int { return os.Getgid() } func (direct) getgid() int { return os.Getgid() }
func (direct) lookupEnv(key string) (string, bool) { return os.LookupEnv(key) } func (direct) lookupEnv(key string) (string, bool) { return os.LookupEnv(key) }
func (direct) pipe() (r, w *os.File, err error) { return os.Pipe() }
func (direct) stat(name string) (os.FileInfo, error) { return os.Stat(name) } func (direct) stat(name string) (os.FileInfo, error) { return os.Stat(name) }
func (direct) open(name string) (osFile, error) { return os.Open(name) } func (direct) open(name string) (osFile, error) { return os.Open(name) }
func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) } func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
func (direct) tempdir() string { return os.TempDir() } func (direct) tempdir() string { return os.TempDir() }
func (direct) exit(code int) { os.Exit(code) }
func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) } func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
@@ -98,11 +132,32 @@ func (direct) lookupGroupId(name string) (gid string, err error) {
func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() } func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() }
func (direct) overflowUid(msg message.Msg) int { return container.OverflowUid(msg) } func (direct) notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
func (direct) overflowGid(msg message.Msg) int { return container.OverflowGid(msg) } return signal.NotifyContext(parent, signals...)
}
func (direct) prctl(op, arg2, arg3 uintptr) error { return container.Prctl(op, arg2, arg3) }
func (direct) overflowUid(msg message.Msg) int { return container.OverflowUid(msg) }
func (direct) overflowGid(msg message.Msg) int { return container.OverflowGid(msg) }
func (direct) setDumpable(dumpable uintptr) error { return container.SetDumpable(dumpable) }
func (direct) receive(key string, e any, fdp *uintptr) (func() error, error) {
return container.Receive(key, e, fdp)
}
func (direct) containerStart(z *container.Container) error { return z.Start() }
func (direct) containerServe(z *container.Container) error { return z.Serve() }
func (direct) containerWait(z *container.Container) error { return z.Wait() }
func (direct) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error {
return seccomp.Load(rules, flags)
}
func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() } func (direct) mustHsuPath() *check.Absolute { return internal.MustHsuPath() }
func (k direct) dbusAddress() (session, system string) { return dbus.Address() } func (direct) dbusAddress() (session, system string) { return dbus.Address() }
func (direct) fatalf(format string, v ...any) { log.Fatalf(format, v...) } func (direct) setupContSignal(pid int) (io.ReadCloser, func(), error) { return setupContSignal(pid) }
func (k direct) getMsg() message.Msg { return k.msg }
func (k direct) fatal(v ...any) { k.msg.GetLogger().Fatal(v...) }
func (k direct) fatalf(format string, v ...any) { k.msg.GetLogger().Fatalf(format, v...) }

View File

@@ -2,6 +2,7 @@ package app
import ( import (
"bytes" "bytes"
"context"
"io" "io"
"io/fs" "io/fs"
"log" "log"
@@ -10,11 +11,14 @@ import (
"os/exec" "os/exec"
"reflect" "reflect"
"slices" "slices"
"sync"
"testing" "testing"
"time" "time"
"unsafe"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/internal/app/state" "hakurei.app/internal/app/state"
@@ -312,6 +316,10 @@ type kstub struct {
*stub.Stub[syscallDispatcher] *stub.Stub[syscallDispatcher]
} }
func (k *kstub) new(f func(k syscallDispatcher, msg message.Msg)) {
k.New(func(k syscallDispatcher) { f(k, k.(*kstub)) })
}
func (k *kstub) getpid() int { k.Helper(); return k.Expects("getpid").Ret.(int) } func (k *kstub) getpid() int { k.Helper(); return k.Expects("getpid").Ret.(int) }
func (k *kstub) getuid() int { k.Helper(); return k.Expects("getuid").Ret.(int) } func (k *kstub) getuid() int { k.Helper(); return k.Expects("getuid").Ret.(int) }
func (k *kstub) getgid() int { k.Helper(); return k.Expects("getgid").Ret.(int) } func (k *kstub) getgid() int { k.Helper(); return k.Expects("getgid").Ret.(int) }
@@ -353,6 +361,61 @@ func (k *kstub) evalSymlinks(path string) (string, error) {
stub.CheckArg(k.Stub, "path", path, 0)) stub.CheckArg(k.Stub, "path", path, 0))
} }
func (k *kstub) prctl(op, arg2, arg3 uintptr) error {
k.Helper()
return k.Expects("prctl").Error(
stub.CheckArg(k.Stub, "op", op, 0),
stub.CheckArg(k.Stub, "arg2", arg2, 1),
stub.CheckArg(k.Stub, "arg3", arg3, 2))
}
func (k *kstub) setDumpable(dumpable uintptr) error {
k.Helper()
return k.Expects("setDumpable").Error(
stub.CheckArg(k.Stub, "dumpable", dumpable, 0))
}
func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error) {
k.Helper()
expect := k.Expects("receive")
reflect.ValueOf(e).Elem().Set(reflect.ValueOf(expect.Args[1]))
if expect.Args[2] != nil {
*fdp = expect.Args[2].(uintptr)
}
return func() error { return k.Expects("closeReceive").Err }, expect.Error(
stub.CheckArg(k.Stub, "key", key, 0))
}
func (k *kstub) expectCheckContainer(expect *stub.Call, z *container.Container) error {
k.Helper()
err := expect.Error(
stub.CheckArgReflect(k.Stub, "params", &z.Params, 0))
if err != nil {
k.Errorf("params:\n%s\n%s", mustMarshal(&z.Params), mustMarshal(expect.Args[0]))
}
return err
}
func (k *kstub) containerStart(z *container.Container) error {
k.Helper()
return k.expectCheckContainer(k.Expects("containerStart"), z)
}
func (k *kstub) containerServe(z *container.Container) error {
k.Helper()
return k.expectCheckContainer(k.Expects("containerServe"), z)
}
func (k *kstub) containerWait(z *container.Container) error {
k.Helper()
return k.expectCheckContainer(k.Expects("containerWait"), z)
}
func (k *kstub) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error {
k.Helper()
return k.Expects("seccompLoad").Error(
stub.CheckArgReflect(k.Stub, "rules", rules, 0),
stub.CheckArg(k.Stub, "flags", flags, 1))
}
func (k *kstub) cmdOutput(cmd *exec.Cmd) ([]byte, error) { func (k *kstub) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
k.Helper() k.Helper()
expect := k.Expects("cmdOutput") expect := k.Expects("cmdOutput")
@@ -363,6 +426,16 @@ func (k *kstub) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
stub.CheckArg(k.Stub, "cmd.Dir", cmd.Dir, 3)) stub.CheckArg(k.Stub, "cmd.Dir", cmd.Dir, 3))
} }
func (k *kstub) notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
k.Helper()
if k.Expects("notifyContext").Error(
stub.CheckArgReflect(k.Stub, "parent", parent, 0),
stub.CheckArgReflect(k.Stub, "signals", signals, 1)) != nil {
k.FailNow()
}
return k.Context(), func() { k.Helper(); k.Expects("notifyContextStop") }
}
func (k *kstub) mustHsuPath() *check.Absolute { func (k *kstub) mustHsuPath() *check.Absolute {
k.Helper() k.Helper()
return k.Expects("mustHsuPath").Ret.(*check.Absolute) return k.Expects("mustHsuPath").Ret.(*check.Absolute)
@@ -374,9 +447,52 @@ func (k *kstub) dbusAddress() (session, system string) {
return ret[0], ret[1] return ret[0], ret[1]
} }
func (k *kstub) GetLogger() *log.Logger { panic("unreachable") } // stubTrackReader embeds kstub but switches the underlying [stub.Stub] index to sub on its first Read.
// The resulting kstub does not share any state with the instance passed to the instrumented goroutine.
// Therefore, any method making use of such must not be called.
type stubTrackReader struct {
sub int
subOnce sync.Once
func (k *kstub) IsVerbose() bool { k.Helper(); return k.Expects("isVerbose").Ret.(bool) } *kstub
}
func (r *stubTrackReader) Read(p []byte) (n int, err error) {
r.subOnce.Do(func() {
subVal := reflect.ValueOf(r.kstub.Stub).Elem().FieldByName("sub")
r.kstub = &kstub{panicDispatcher{}, reflect.
NewAt(subVal.Type(), unsafe.Pointer(subVal.UnsafeAddr())).Elem().
Interface().([]*stub.Stub[syscallDispatcher])[r.sub]}
})
return r.kstub.Read(p)
}
func (k *kstub) setupContSignal(pid int) (io.ReadCloser, func(), error) {
k.Helper()
expect := k.Expects("setupContSignal")
return &stubTrackReader{sub: expect.Ret.(int), kstub: k}, func() { k.Expects("wKeepAlive") }, expect.Error(
stub.CheckArg(k.Stub, "pid", pid, 0))
}
func (k *kstub) getMsg() message.Msg { k.Helper(); k.Expects("getMsg"); return k }
func (k *kstub) Close() error { k.Helper(); return k.Expects("rcClose").Err }
func (k *kstub) Read(p []byte) (n int, err error) {
k.Helper()
expect := k.Expects("rcRead")
// special case to terminate exit outcomes goroutine
// to proceed with further testing of the entrypoint
if expect.Ret == nil {
panic(stub.PanicExit)
}
return copy(p, expect.Ret.([]byte)), expect.Err
}
func (k *kstub) GetLogger() *log.Logger { k.Helper(); return k.Expects("getLogger").Ret.(*log.Logger) }
func (k *kstub) IsVerbose() bool { k.Helper(); return k.Expects("isVerbose").Ret.(bool) }
func (k *kstub) SwapVerbose(verbose bool) bool { func (k *kstub) SwapVerbose(verbose bool) bool {
k.Helper() k.Helper()
expect := k.Expects("swapVerbose") expect := k.Expects("swapVerbose")
@@ -492,20 +608,38 @@ func (panicMsgContext) Value(any) any { panic("unreachable") }
// This type is meant to be embedded in partial syscallDispatcher implementations. // This type is meant to be embedded in partial syscallDispatcher implementations.
type panicDispatcher struct{} type panicDispatcher struct{}
func (panicDispatcher) new(func(k syscallDispatcher)) { panic("unreachable") } func (panicDispatcher) new(func(k syscallDispatcher, msg message.Msg)) { panic("unreachable") }
func (panicDispatcher) getpid() int { panic("unreachable") } func (panicDispatcher) getpid() int { panic("unreachable") }
func (panicDispatcher) getuid() int { panic("unreachable") } func (panicDispatcher) getuid() int { panic("unreachable") }
func (panicDispatcher) getgid() int { panic("unreachable") } func (panicDispatcher) getgid() int { panic("unreachable") }
func (panicDispatcher) lookupEnv(string) (string, bool) { panic("unreachable") } func (panicDispatcher) lookupEnv(string) (string, bool) { panic("unreachable") }
func (panicDispatcher) stat(string) (os.FileInfo, error) { panic("unreachable") } func (panicDispatcher) pipe() (*os.File, *os.File, error) { panic("unreachable") }
func (panicDispatcher) open(string) (osFile, error) { panic("unreachable") } func (panicDispatcher) stat(string) (os.FileInfo, error) { panic("unreachable") }
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") } func (panicDispatcher) open(string) (osFile, error) { panic("unreachable") }
func (panicDispatcher) tempdir() string { panic("unreachable") } func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") } func (panicDispatcher) tempdir() string { panic("unreachable") }
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") } func (panicDispatcher) exit(int) { panic("unreachable") }
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") } func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") } func (panicDispatcher) prctl(uintptr, uintptr, uintptr) error { panic("unreachable") }
func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") } func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
func (panicDispatcher) mustHsuPath() *check.Absolute { panic("unreachable") } func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
func (panicDispatcher) dbusAddress() (string, string) { panic("unreachable") } func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") }
func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") } func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") }
func (panicDispatcher) setDumpable(uintptr) error { panic("unreachable") }
func (panicDispatcher) receive(string, any, *uintptr) (func() error, error) { panic("unreachable") }
func (panicDispatcher) containerStart(*container.Container) error { panic("unreachable") }
func (panicDispatcher) containerServe(*container.Container) error { panic("unreachable") }
func (panicDispatcher) containerWait(*container.Container) error { panic("unreachable") }
func (panicDispatcher) mustHsuPath() *check.Absolute { panic("unreachable") }
func (panicDispatcher) dbusAddress() (string, string) { panic("unreachable") }
func (panicDispatcher) setupContSignal(int) (io.ReadCloser, func(), error) { panic("unreachable") }
func (panicDispatcher) getMsg() message.Msg { panic("unreachable") }
func (panicDispatcher) fatal(...any) { panic("unreachable") }
func (panicDispatcher) fatalf(string, ...any) { panic("unreachable") }
func (panicDispatcher) notifyContext(context.Context, ...os.Signal) (context.Context, context.CancelFunc) {
panic("unreachable")
}
func (panicDispatcher) seccompLoad([]seccomp.NativeRule, seccomp.ExportFlag) error {
panic("unreachable")
}

View File

@@ -21,7 +21,9 @@ type Hsu struct {
id int id int
kOnce sync.Once kOnce sync.Once
k syscallDispatcher
// msg is not populated
k syscallDispatcher
} }
var ErrHsuAccess = errors.New("current user is not in the hsurc file") var ErrHsuAccess = errors.New("current user is not in the hsurc file")

View File

@@ -7,14 +7,13 @@ import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"runtime" "runtime"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
"time" "time"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/message" "hakurei.app/message"
@@ -23,6 +22,20 @@ import (
//#include "shim-signal.h" //#include "shim-signal.h"
import "C" import "C"
// setupContSignal sets up the SIGCONT signal handler for the cross-uid shim exit hack.
// The signal handler is implemented in C, signals can be processed by reading from the returned reader.
// The returned function must be called after all signal processing concludes.
func setupContSignal(pid int) (io.ReadCloser, func(), error) {
if r, w, err := os.Pipe(); err != nil {
return nil, nil, err
} else if _, err = C.hakurei_shim_setup_cont_signal(C.pid_t(pid), C.int(w.Fd())); err != nil {
_, _ = r.Close(), w.Close()
return nil, nil, err
} else {
return r, func() { runtime.KeepAlive(w) }, nil
}
}
// shimEnv is the name of the environment variable storing decimal representation of // shimEnv is the name of the environment variable storing decimal representation of
// setup pipe fd for [container.Receive]. // setup pipe fd for [container.Receive].
const shimEnv = "HAKUREI_SHIM" const shimEnv = "HAKUREI_SHIM"
@@ -46,76 +59,102 @@ type shimParams struct {
// valid checks shimParams to be safe for use. // valid checks shimParams to be safe for use.
func (p *shimParams) valid() bool { return p != nil && p.PrivPID > 0 } func (p *shimParams) valid() bool { return p != nil && p.PrivPID > 0 }
// ShimMain is the main function of the shim process and runs as the unconstrained target user. // shimName is the prefix used by log.std in the shim process.
func ShimMain() { const shimName = "shim"
log.SetPrefix("shim: ")
log.SetFlags(0)
msg := message.NewMsg(log.Default())
if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil { // Shim is called by the main function of the shim process and runs as the unconstrained target user.
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err) // Shim does not return.
func Shim(msg message.Msg) {
if msg == nil {
msg = message.NewMsg(log.Default())
}
shimEntrypoint(direct{msg})
}
func shimEntrypoint(k syscallDispatcher) {
msg := k.getMsg()
if msg == nil {
panic("attempting to call shimEntrypoint with nil msg")
} else if logger := msg.GetLogger(); logger != nil {
logger.SetPrefix(shimName + ": ")
logger.SetFlags(0)
}
if err := k.setDumpable(container.SUID_DUMP_DISABLE); err != nil {
k.fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
} }
var ( var (
state outcomeState state outcomeState
closeSetup func() error closeSetup func() error
) )
if f, err := container.Receive(shimEnv, &state, nil); err != nil { if f, err := k.receive(shimEnv, &state, nil); err != nil {
if errors.Is(err, syscall.EBADF) { if errors.Is(err, syscall.EBADF) {
log.Fatal("invalid config descriptor") k.fatal("invalid config descriptor")
} }
if errors.Is(err, container.ErrReceiveEnv) { if errors.Is(err, container.ErrReceiveEnv) {
log.Fatal(shimEnv + " not set") k.fatal(shimEnv + " not set")
} }
log.Fatalf("cannot receive shim setup params: %v", err) k.fatalf("cannot receive shim setup params: %v", err)
} else { } else {
msg.SwapVerbose(state.Shim.Verbose) msg.SwapVerbose(state.Shim.Verbose)
closeSetup = f closeSetup = f
if err = state.populateLocal(direct{}, msg); err != nil { if err = state.populateLocal(k, msg); err != nil {
if m, ok := message.GetMessage(err); ok { if m, ok := message.GetMessage(err); ok {
log.Fatal(m) k.fatal(m)
} else { } else {
log.Fatalf("cannot populate local state: %v", err) k.fatalf("cannot populate local state: %v", err)
} }
} }
} }
// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid // the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid
var signalPipe io.ReadCloser var signalPipe io.ReadCloser
if r, w, err := os.Pipe(); err != nil { if r, wKeepAlive, err := k.setupContSignal(state.Shim.PrivPID); err != nil {
log.Fatalf("cannot pipe: %v", err) switch {
} else if _, err = C.hakurei_shim_setup_cont_signal(C.pid_t(state.Shim.PrivPID), C.int(w.Fd())); err != nil { case errors.As(err, new(*os.SyscallError)): // returned by os.Pipe
log.Fatalf("cannot install SIGCONT handler: %v", err) k.fatal(err.Error())
return
case errors.As(err, new(syscall.Errno)): // returned by hakurei_shim_setup_cont_signal
k.fatalf("cannot install SIGCONT handler: %v", err)
return
default: // unreachable
k.fatalf("cannot set up exit request: %v", err)
return
}
} else { } else {
defer runtime.KeepAlive(w) defer wKeepAlive()
signalPipe = r signalPipe = r
} }
// pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c // pdeath_signal delivery is checked as if the dying process called kill(2), see kernel/exit.c
if _, _, errno := syscall.Syscall(syscall.SYS_PRCTL, syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); errno != 0 { if err := k.prctl(syscall.PR_SET_PDEATHSIG, uintptr(syscall.SIGCONT), 0); err != nil {
log.Fatalf("cannot set parent-death signal: %v", errno) k.fatalf("cannot set parent-death signal: %v", err)
} }
stateParams := state.newParams() stateParams := state.newParams()
for _, op := range state.Shim.Ops { for _, op := range state.Shim.Ops {
if err := op.toContainer(stateParams); err != nil { if err := op.toContainer(stateParams); err != nil {
if m, ok := message.GetMessage(err); ok { if m, ok := message.GetMessage(err); ok {
log.Fatal(m) k.fatal(m)
} else { } else {
log.Fatalf("cannot create container state: %v", err) k.fatalf("cannot create container state: %v", err)
} }
} }
} }
// shim exit outcomes // shim exit outcomes
var cancelContainer atomic.Pointer[context.CancelFunc] var cancelContainer atomic.Pointer[context.CancelFunc]
go func() { k.new(func(k syscallDispatcher, msg message.Msg) {
buf := make([]byte, 1) buf := make([]byte, 1)
for { for {
if _, err := signalPipe.Read(buf); err != nil { if _, err := signalPipe.Read(buf); err != nil {
log.Fatalf("cannot read from signal pipe: %v", err) k.fatalf("cannot read from signal pipe: %v", err)
} }
switch buf[0] { switch buf[0] {
@@ -128,37 +167,37 @@ func ShimMain() {
// setup has not completed, terminate immediately // setup has not completed, terminate immediately
msg.Resume() msg.Resume()
os.Exit(hst.ExitRequest) k.exit(hst.ExitRequest)
return return
case 1: // got SIGCONT after adoption: monitor died before delivering signal case 1: // got SIGCONT after adoption: monitor died before delivering signal
msg.BeforeExit() msg.BeforeExit()
os.Exit(hst.ExitOrphan) k.exit(hst.ExitOrphan)
return return
case 2: // unreachable case 2: // unreachable
log.Println("sa_sigaction got invalid siginfo") msg.Verbose("sa_sigaction got invalid siginfo")
case 3: // got SIGCONT from unexpected process: hopefully the terminal driver case 3: // got SIGCONT from unexpected process: hopefully the terminal driver
log.Println("got SIGCONT from unexpected process") msg.Verbose("got SIGCONT from unexpected process")
default: // unreachable default: // unreachable
log.Fatalf("got invalid message %d from signal handler", buf[0]) k.fatalf("got invalid message %d from signal handler", buf[0])
} }
} }
}() })
if stateParams.params.Ops == nil { if stateParams.params.Ops == nil {
log.Fatal("invalid container params") k.fatal("invalid container params")
} }
// close setup socket // close setup socket
if err := closeSetup(); err != nil { if err := closeSetup(); err != nil {
log.Printf("cannot close setup pipe: %v", err) msg.Verbosef("cannot close setup pipe: %v", err)
// not fatal // not fatal
} }
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) ctx, stop := k.notifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
cancelContainer.Store(&stop) cancelContainer.Store(&stop)
z := container.New(ctx, msg) z := container.New(ctx, msg)
z.Params = *stateParams.params z.Params = *stateParams.params
@@ -167,30 +206,30 @@ func ShimMain() {
// bounds and default enforced in finalise.go // bounds and default enforced in finalise.go
z.WaitDelay = state.Shim.WaitDelay z.WaitDelay = state.Shim.WaitDelay
if err := z.Start(); err != nil { if err := k.containerStart(z); err != nil {
printMessageError("cannot start container:", err) printMessageError("cannot start container:", err)
os.Exit(hst.ExitFailure) k.exit(hst.ExitFailure)
} }
if err := z.Serve(); err != nil { if err := k.containerServe(z); err != nil {
printMessageError("cannot configure container:", err) printMessageError("cannot configure container:", err)
} }
if err := seccomp.Load( if err := k.seccompLoad(
seccomp.Preset(bits.PresetStrict, seccomp.AllowMultiarch), seccomp.Preset(comp.PresetStrict, seccomp.AllowMultiarch),
seccomp.AllowMultiarch, seccomp.AllowMultiarch,
); err != nil { ); err != nil {
log.Fatalf("cannot load syscall filter: %v", err) k.fatalf("cannot load syscall filter: %v", err)
} }
if err := z.Wait(); err != nil { if err := k.containerWait(z); err != nil {
var exitError *exec.ExitError var exitError *exec.ExitError
if !errors.As(err, &exitError) { if !errors.As(err, &exitError) {
if errors.Is(err, context.Canceled) { if errors.Is(err, context.Canceled) {
os.Exit(hst.ExitCancel) k.exit(hst.ExitCancel)
} }
log.Printf("wait: %v", err) msg.Verbosef("cannot wait: %v", err)
os.Exit(127) k.exit(127)
} }
os.Exit(exitError.ExitCode()) k.exit(exitError.ExitCode())
} }
} }

155
internal/app/shim_test.go Normal file
View File

@@ -0,0 +1,155 @@
package app
import (
"bytes"
"context"
"log"
"os"
"syscall"
"testing"
"hakurei.app/container"
"hakurei.app/container/comp"
"hakurei.app/container/fhs"
"hakurei.app/container/seccomp"
"hakurei.app/container/stub"
"hakurei.app/hst"
)
func TestShimEntrypoint(t *testing.T) {
t.Parallel()
shimPreset := seccomp.Preset(comp.PresetStrict, seccomp.AllowMultiarch)
templateParams := &container.Params{
Dir: m("/data/data/org.chromium.Chromium"),
Env: []string{
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus",
"DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket",
"GOOGLE_API_KEY=AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
"GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com",
"GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT",
"HOME=/data/data/org.chromium.Chromium",
"PULSE_COOKIE=/.hakurei/pulse-cookie",
"PULSE_SERVER=unix:/run/user/1000/pulse/native",
"SHELL=/run/current-system/sw/bin/zsh",
"TERM=xterm-256color",
"USER=chronos",
"WAYLAND_DISPLAY=wayland-0",
"XDG_RUNTIME_DIR=/run/user/1000",
"XDG_SESSION_CLASS=user",
"XDG_SESSION_TYPE=wayland",
},
// spParamsOp
Hostname: "localhost",
RetainSession: true,
HostNet: true,
HostAbstract: true,
ForwardCancel: true,
Path: m("/run/current-system/sw/bin/chromium"),
Args: []string{
"chromium",
"--ignore-gpu-blocklist",
"--disable-smooth-scrolling",
"--enable-features=UseOzonePlatform",
"--ozone-platform=wayland",
},
SeccompFlags: seccomp.AllowMultiarch,
Uid: 1000,
Gid: 100,
Ops: new(container.Ops).
// resolveRoot
Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
// spParamsOp
Proc(fhs.AbsProc).
Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777).
// spRuntimeOp
Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/tmp/hakurei.10/runtime/9999"), m("/run/user/1000"), comp.BindWritable).
// spTmpdirOp
Bind(m("/tmp/hakurei.10/tmpdir/9999"), fhs.AbsTmp, comp.BindWritable).
// spAccountOp
Place(m("/etc/passwd"), []byte("chronos:x:1000:100:Hakurei:/data/data/org.chromium.Chromium:/run/current-system/sw/bin/zsh\n")).
Place(m("/etc/group"), []byte("hakurei:x:100:\n")).
// spWaylandOp
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1000/wayland-0"), 0).
// spPulseOp
Bind(m("/run/user/1000/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse"), m("/run/user/1000/pulse/native"), 0).
Place(m("/.hakurei/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)).
// spDBusOp
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1000/bus"), 0).
Bind(m("/tmp/hakurei.10/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
// spFilesystemOp
Etc(fhs.AbsEtc, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").
Tmpfs(fhs.AbsTmp, 0, 0755).
Overlay(m("/nix/store"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/upper"),
fhs.AbsVarLib.Append("hakurei/nix/u0/org.chromium.Chromium/rw-store/work"),
fhs.AbsVarLib.Append("hakurei/base/org.nixos/ro-store")).
Link(m("/run/current-system"), "/run/current-system", true).
Link(m("/run/opengl-driver"), "/run/opengl-driver", true).
Bind(fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"),
m("/data/data/org.chromium.Chromium"),
comp.BindWritable|comp.BindEnsure).
Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"),
comp.BindOptional|comp.BindWritable|comp.BindDevice).
Remount(fhs.AbsRoot, syscall.MS_RDONLY),
}
checkSimple(t, "shimEntrypoint", []simpleTestCase{
{"success", func(k *kstub) error { shimEntrypoint(k); return nil }, stub.Expect{Calls: []stub.Call{
call("getMsg", stub.ExpectArgs{}, nil, nil),
call("getLogger", stub.ExpectArgs{}, (*log.Logger)(nil), nil),
call("setDumpable", stub.ExpectArgs{uintptr(container.SUID_DUMP_DISABLE)}, nil, nil),
call("receive", stub.ExpectArgs{"HAKUREI_SHIM", outcomeState{
Shim: &shimParams{PrivPID: 0xbad, WaitDelay: 0xf, Verbose: true, Ops: []outcomeOp{
&spParamsOp{"xterm-256color", true},
&spRuntimeOp{sessionTypeWayland},
spTmpdirOp{},
spAccountOp{},
&spWaylandOp{},
&spPulseOp{(*[256]byte)(bytes.Repeat([]byte{0}, pulseCookieSizeMax)), pulseCookieSizeMax},
&spDBusOp{true},
&spFilesystemOp{},
}},
ID: &checkExpectInstanceId,
Identity: hst.IdentityMax,
UserID: 10,
Container: hst.Template().Container,
Mapuid: 1000,
Mapgid: 100,
EnvPaths: &EnvPaths{TempDir: fhs.AbsTmp, RuntimePath: fhs.AbsRunUser.Append("1000")},
}, nil}, nil, nil),
call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbosef", stub.ExpectArgs{"process share directory at %q, runtime directory at %q", []any{m("/tmp/hakurei.10"), m("/run/user/1000/hakurei")}}, nil, nil),
call("setupContSignal", stub.ExpectArgs{0xbad}, 0, nil),
call("prctl", stub.ExpectArgs{uintptr(syscall.PR_SET_PDEATHSIG), uintptr(syscall.SIGCONT), uintptr(0)}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("closeReceive", stub.ExpectArgs{}, nil, nil),
call("notifyContext", stub.ExpectArgs{context.Background(), []os.Signal{os.Interrupt, syscall.SIGTERM}}, nil, nil),
call("containerStart", stub.ExpectArgs{templateParams}, nil, nil),
call("containerServe", stub.ExpectArgs{templateParams}, nil, nil),
call("seccompLoad", stub.ExpectArgs{shimPreset, seccomp.AllowMultiarch}, nil, nil),
call("containerWait", stub.ExpectArgs{templateParams}, nil, nil),
// deferred
call("wKeepAlive", stub.ExpectArgs{}, nil, nil),
}, Tracks: []stub.Expect{{Calls: []stub.Call{
call("rcRead", stub.ExpectArgs{}, []byte{2}, nil),
call("verbose", stub.ExpectArgs{[]any{"sa_sigaction got invalid siginfo"}}, nil, nil),
call("rcRead", stub.ExpectArgs{}, []byte{3}, nil),
call("verbose", stub.ExpectArgs{[]any{"got SIGCONT from unexpected process"}}, nil, nil),
call("rcRead", stub.ExpectArgs{}, nil, nil), // stub terminates this goroutine
}}}}, nil},
})
}

View File

@@ -11,8 +11,8 @@ import (
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/hst" "hakurei.app/hst"
@@ -75,16 +75,16 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
} }
if state.Container.Flags&hst.FSeccompCompat == 0 { if state.Container.Flags&hst.FSeccompCompat == 0 {
state.params.SeccompPresets |= bits.PresetExt state.params.SeccompPresets |= comp.PresetExt
} }
if state.Container.Flags&hst.FDevel == 0 { if state.Container.Flags&hst.FDevel == 0 {
state.params.SeccompPresets |= bits.PresetDenyDevel state.params.SeccompPresets |= comp.PresetDenyDevel
} }
if state.Container.Flags&hst.FUserns == 0 { if state.Container.Flags&hst.FUserns == 0 {
state.params.SeccompPresets |= bits.PresetDenyNS state.params.SeccompPresets |= comp.PresetDenyNS
} }
if state.Container.Flags&hst.FTty == 0 { if state.Container.Flags&hst.FTty == 0 {
state.params.SeccompPresets |= bits.PresetDenyTTY state.params.SeccompPresets |= comp.PresetDenyTTY
} }
if state.Container.Flags&hst.FMapRealUID != 0 { if state.Container.Flags&hst.FMapRealUID != 0 {
@@ -112,7 +112,7 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
if state.Container.Flags&hst.FDevice == 0 { if state.Container.Flags&hst.FDevice == 0 {
state.params.DevWritable(fhs.AbsDev, true) state.params.DevWritable(fhs.AbsDev, true)
} else { } else {
state.params.Bind(fhs.AbsDev, fhs.AbsDev, bits.BindWritable|bits.BindDevice) state.params.Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice)
} }
// /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it // /dev is mounted readonly later on, this prevents /dev/shm from going readonly with it
state.params.Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777) state.params.Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777)

View File

@@ -8,8 +8,8 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/container/stub" "hakurei.app/container/stub"
@@ -65,11 +65,11 @@ func TestSpParamsOp(t *testing.T) {
HostAbstract: true, HostAbstract: true,
Path: config.Container.Path, Path: config.Container.Path,
Args: []string{config.Container.Path.String()}, Args: []string{config.Container.Path.String()},
SeccompPresets: bits.PresetExt | bits.PresetDenyDevel | bits.PresetDenyNS | bits.PresetDenyTTY, SeccompPresets: comp.PresetExt | comp.PresetDenyDevel | comp.PresetDenyNS | comp.PresetDenyTTY,
Uid: 1000, Uid: 1000,
Gid: 100, Gid: 100,
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/var/lib/hakurei/base/org.debian"), bits.BindWritable). Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
DevWritable(fhs.AbsDev, true). DevWritable(fhs.AbsDev, true).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777), Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
@@ -107,9 +107,9 @@ func TestSpParamsOp(t *testing.T) {
Uid: 1000, Uid: 1000,
Gid: 100, Gid: 100,
Ops: new(container.Ops). Ops: new(container.Ops).
Root(m("/var/lib/hakurei/base/org.debian"), bits.BindWritable). Root(m("/var/lib/hakurei/base/org.debian"), comp.BindWritable).
Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755). Proc(fhs.AbsProc).Tmpfs(hst.AbsPrivateTmp, 1<<12, 0755).
Bind(fhs.AbsDev, fhs.AbsDev, bits.BindWritable|bits.BindDevice). Bind(fhs.AbsDev, fhs.AbsDev, comp.BindWritable|comp.BindDevice).
Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777), Tmpfs(fhs.AbsDev.Append("shm"), 0, 01777),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"TERM": "xterm", "TERM": "xterm",
@@ -425,8 +425,8 @@ func TestSpFilesystemOp(t *testing.T) {
Bind( Bind(
fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"), fhs.AbsVarLib.Append("hakurei/u0/org.chromium.Chromium"),
check.MustAbs("/data/data/org.chromium.Chromium"), check.MustAbs("/data/data/org.chromium.Chromium"),
bits.BindWritable|bits.BindEnsure). comp.BindWritable|comp.BindEnsure).
Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"), bits.BindDevice|bits.BindWritable|bits.BindOptional). Bind(fhs.AbsDev.Append("dri"), fhs.AbsDev.Append("dri"), comp.BindDevice|comp.BindWritable|comp.BindOptional).
Remount(fhs.AbsRoot, syscall.MS_RDONLY), Remount(fhs.AbsRoot, syscall.MS_RDONLY),
}, nil, nil}, }, nil, nil},
}) })

View File

@@ -3,8 +3,8 @@ package app
import ( import (
"encoding/gob" "encoding/gob"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
@@ -111,7 +111,7 @@ func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error {
state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755) state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755)
if state.Container.Flags&hst.FShareRuntime != 0 { if state.Container.Flags&hst.FShareRuntime != 0 {
_, runtimeDirInst := s.commonPaths(state.outcomeState) _, runtimeDirInst := s.commonPaths(state.outcomeState)
state.params.Bind(runtimeDirInst, state.runtimeDir, bits.BindWritable) state.params.Bind(runtimeDirInst, state.runtimeDir, comp.BindWritable)
} else { } else {
state.params.Mkdir(state.runtimeDir, 0700) state.params.Mkdir(state.runtimeDir, 0700)
} }

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
@@ -41,7 +41,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
@@ -68,7 +68,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
@@ -95,7 +95,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",
@@ -118,7 +118,7 @@ func TestSpRuntimeOp(t *testing.T) {
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Tmpfs(fhs.AbsRunUser, 1<<12, 0755). Tmpfs(fhs.AbsRunUser, 1<<12, 0755).
Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), comp.BindWritable),
}, paramsWantEnv(config, map[string]string{ }, paramsWantEnv(config, map[string]string{
"XDG_RUNTIME_DIR": "/run/user/1000", "XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_CLASS": "user", "XDG_SESSION_CLASS": "user",

View File

@@ -3,8 +3,8 @@ package app
import ( import (
"encoding/gob" "encoding/gob"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/hst" "hakurei.app/hst"
"hakurei.app/system" "hakurei.app/system"
@@ -30,7 +30,7 @@ func (s spTmpdirOp) toSystem(state *outcomeStateSys) error {
func (s spTmpdirOp) toContainer(state *outcomeStateParams) error { func (s spTmpdirOp) toContainer(state *outcomeStateParams) error {
if state.Container.Flags&hst.FShareTmpdir != 0 { if state.Container.Flags&hst.FShareTmpdir != 0 {
_, tmpdirInst := s.commonPaths(state.outcomeState) _, tmpdirInst := s.commonPaths(state.outcomeState)
state.params.Bind(tmpdirInst, fhs.AbsTmp, bits.BindWritable) state.params.Bind(tmpdirInst, fhs.AbsTmp, comp.BindWritable)
} else { } else {
state.params.Tmpfs(fhs.AbsTmp, 0, 01777) state.params.Tmpfs(fhs.AbsTmp, 0, 01777)
} }

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits" "hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/stub" "hakurei.app/container/stub"
"hakurei.app/hst" "hakurei.app/hst"
@@ -28,7 +28,7 @@ func TestSpTmpdirOp(t *testing.T) {
// this op configures the container state and does not make calls during toContainer // this op configures the container state and does not make calls during toContainer
}, &container.Params{ }, &container.Params{
Ops: new(container.Ops). Ops: new(container.Ops).
Bind(m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, bits.BindWritable), Bind(m("/proc/nonexistent/tmp/hakurei.0/tmpdir/9"), fhs.AbsTmp, comp.BindWritable),
}, nil, nil}, }, nil, nil},
}) })
} }

View File

@@ -1,17 +0,0 @@
package internal
const compPoison = "INVALIDINVALIDINVALIDINVALIDINVALID"
var (
version = compPoison
)
// checkComp validates string value set at compile time.
func checkComp(s string) (string, bool) { return s, s != compPoison && s != "" }
func Version() string {
if v, ok := checkComp(version); ok {
return v
}
return "impure"
}

View File

@@ -6,20 +6,20 @@ import (
"hakurei.app/container/check" "hakurei.app/container/check"
) )
var ( // Absolute paths to the Hakurei installation.
hmain = compPoison //
hsu = compPoison // These are set by the linker.
) var hakureiPath, hsuPath string
// MustHakureiPath returns the absolute path to hakurei, configured at compile time. // MustHakureiPath returns the [check.Absolute] path to hakurei.
func MustHakureiPath() *check.Absolute { return mustCheckPath(log.Fatal, "hakurei", hmain) } func MustHakureiPath() *check.Absolute { return mustCheckPath(log.Fatal, "hakurei", hakureiPath) }
// MustHsuPath returns the absolute path to hakurei, configured at compile time. // MustHsuPath returns the [check.Absolute] to hsu.
func MustHsuPath() *check.Absolute { return mustCheckPath(log.Fatal, "hsu", hsu) } func MustHsuPath() *check.Absolute { return mustCheckPath(log.Fatal, "hsu", hsuPath) }
// mustCheckPath checks a pathname against compPoison, then [container.NewAbs], calling fatal if either step fails. // mustCheckPath checks a pathname to not be zero, then [check.NewAbs], calling fatal if either step fails.
func mustCheckPath(fatal func(v ...any), name, pathname string) *check.Absolute { func mustCheckPath(fatal func(v ...any), name, pathname string) *check.Absolute {
if pathname != compPoison && pathname != "" { if pathname != "" {
if a, err := check.NewAbs(pathname); err != nil { if a, err := check.NewAbs(pathname); err != nil {
fatal(err.Error()) fatal(err.Error())
return nil // unreachable return nil // unreachable

View File

@@ -15,7 +15,6 @@ func TestMustCheckPath(t *testing.T) {
pathname string pathname string
wantFatal string wantFatal string
}{ }{
{"poison", compPoison, "invalid test path, this program is compiled incorrectly"},
{"zero", "", "invalid test path, this program is compiled incorrectly"}, {"zero", "", "invalid test path, this program is compiled incorrectly"},
{"not absolute", "\x00", `path "\x00" is not absolute`}, {"not absolute", "\x00", `path "\x00" is not absolute`},
{"success", "/proc/nonexistent", ""}, {"success", "/proc/nonexistent", ""},

19
internal/version.go Normal file
View File

@@ -0,0 +1,19 @@
package internal
// FallbackVersion is returned when a version string was not set by the linker.
const FallbackVersion = "dirty"
// buildVersion is the Hakurei tree's version string at build time.
//
// This is set by the linker.
var buildVersion string
// Version returns the Hakurei tree's version string.
// It is either the value of the constant [FallbackVersion] or,
// when possible, a release tag like "v1.0.0".
func Version() string {
if buildVersion != "" {
return buildVersion
}
return FallbackVersion
}

View File

@@ -9,8 +9,8 @@ import (
"time" "time"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/fhs" "hakurei.app/container/fhs"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/message" "hakurei.app/message"
@@ -40,7 +40,7 @@ func Exec(ctx context.Context, msg message.Msg, p string) ([]*Entry, error) {
z := container.NewCommand(c, msg, toolPath, lddName, p) z := container.NewCommand(c, msg, toolPath, lddName, p)
z.Hostname = "hakurei-" + lddName z.Hostname = "hakurei-" + lddName
z.SeccompFlags |= seccomp.AllowMultiarch z.SeccompFlags |= seccomp.AllowMultiarch
z.SeccompPresets |= bits.PresetStrict z.SeccompPresets |= comp.PresetStrict
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
z.Stdout = stdout z.Stdout = stdout
z.Stderr = stderr z.Stderr = stderr

View File

@@ -75,9 +75,9 @@ buildGoModule rec {
] ]
) )
{ {
version = "v${version}"; buildVersion = "v${version}";
hmain = "${placeholder "out"}/libexec/hakurei"; hakureiPath = "${placeholder "out"}/libexec/hakurei";
hsu = "/run/wrappers/bin/hsu"; hsuPath = "/run/wrappers/bin/hsu";
}; };
# nix build environment does not allow acls # nix build environment does not allow acls

View File

@@ -9,8 +9,8 @@ import (
"syscall" "syscall"
"hakurei.app/container" "hakurei.app/container"
"hakurei.app/container/bits"
"hakurei.app/container/check" "hakurei.app/container/check"
"hakurei.app/container/comp"
"hakurei.app/container/seccomp" "hakurei.app/container/seccomp"
"hakurei.app/helper" "hakurei.app/helper"
"hakurei.app/ldd" "hakurei.app/ldd"
@@ -65,7 +65,7 @@ func (p *Proxy) Start() error {
p.final, true, p.final, true,
argF, func(z *container.Container) { argF, func(z *container.Container) {
z.SeccompFlags |= seccomp.AllowMultiarch z.SeccompFlags |= seccomp.AllowMultiarch
z.SeccompPresets |= bits.PresetStrict z.SeccompPresets |= comp.PresetStrict
z.Hostname = "hakurei-dbus" z.Hostname = "hakurei-dbus"
if p.output != nil { if p.output != nil {
z.Stdout, z.Stderr = p.output, p.output z.Stdout, z.Stderr = p.output, p.output
@@ -114,7 +114,7 @@ func (p *Proxy) Start() error {
check.SortAbs(sockDirPaths) check.SortAbs(sockDirPaths)
sockDirPaths = check.CompactAbs(sockDirPaths) sockDirPaths = check.CompactAbs(sockDirPaths)
for _, name := range sockDirPaths { for _, name := range sockDirPaths {
z.Bind(name, name, bits.BindWritable) z.Bind(name, name, comp.BindWritable)
} }
// xdg-dbus-proxy bin path // xdg-dbus-proxy bin path

View File

@@ -32,6 +32,16 @@ nixosTest {
environment.systemPackages = [ environment.systemPackages = [
# For go tests: # For go tests:
(writeShellScriptBin "hakurei-test" '' (writeShellScriptBin "hakurei-test" ''
# Assert hst CGO_ENABLED=0: ${
with pkgs;
runCommand "hakurei-hst-cgo" { nativeBuildInputs = [ go ]; } ''
cp -r ${options.environment.hakurei.package.default.src} "$out"
chmod -R +w "$out"
cp ${writeText "hst_cgo_test.go" ''package hakurei_test;import("testing";"hakurei.app/hst");func TestTemplate(t *testing.T){hst.Template()}''} "$out/hst_cgo_test.go"
(cd "$out" && HOME="$(mktemp -d)" CGO_ENABLED=0 go test .)
''
}
cd ${self.packages.${system}.hakurei.src} cd ${self.packages.${system}.hakurei.src}
${fhs}/bin/hakurei-fhs -c \ ${fhs}/bin/hakurei-fhs -c \
'go test ${if withRace then "-race" else "-count 16"} ./...' \ 'go test ${if withRace then "-race" else "-count 16"} ./...' \

View File

@@ -22,7 +22,7 @@ in
{ {
name = "funcgraph-retval"; name = "funcgraph-retval";
patch = null; patch = null;
extraStructuredConfig.FUNCTION_GRAPH_RETVAL = lib.kernel.yes; structuredExtraConfig.FUNCTION_GRAPH_RETVAL = lib.kernel.yes;
} }
]; ];
} }