cmd/hakurei: move to cmd
All checks were successful
Test / Create distribution (push) Successful in 31s
Test / Sandbox (push) Successful in 1m50s
Test / Hakurei (push) Successful in 3m2s
Test / Sandbox (race detector) (push) Successful in 3m18s
Test / Planterette (push) Successful in 3m36s
Test / Hakurei (race detector) (push) Successful in 4m35s
Test / Flake checks (push) Successful in 1m7s

Having it at the project root never made sense since the "ego" name was deprecated. This change finally addresses it.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-07-02 20:42:51 +09:00
parent 31aef905fa
commit eb22a8bcc1
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
42 changed files with 79 additions and 73 deletions

1
.gitignore vendored
View File

@ -27,6 +27,7 @@ go.work.sum
# go generate
security-context-v1-protocol.*
/cmd/hakurei/LICENSE
# release
/dist/hakurei-*

View File

@ -1,4 +1,4 @@
Copyright (c) 2024 Ophestra Umiker
Copyright (c) 2024-2025 Ophestra
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -47,13 +47,3 @@ func (rs *RunState) SetStart() {
now := time.Now().UTC()
rs.Time = &now
}
// Paths contains environment-dependent paths used by hakurei.
type Paths struct {
// path to shared directory (usually `/tmp/hakurei.%d`)
SharePath string `json:"share_path"`
// XDG_RUNTIME_DIR value (usually `/run/user/%d`)
RuntimePath string `json:"runtime_path"`
// application runtime directory (usually `/run/user/%d/hakurei`)
RunDirPath string `json:"run_dir_path"`
}

View File

@ -4,7 +4,7 @@ import (
"errors"
"testing"
. "git.gensokyo.uk/security/hakurei/internal/app"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
)
func TestParseAppID(t *testing.T) {

View File

@ -3,8 +3,8 @@ package instance
import (
"syscall"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app/internal/setuid"
)
func PrintRunStateErr(whence int, rs *app.RunState, runErr error) (code int) {

View File

@ -6,8 +6,8 @@ import (
"log"
"syscall"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app/internal/setuid"
"git.gensokyo.uk/security/hakurei/internal/sys"
)

View File

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

View File

@ -5,8 +5,8 @@ import (
"fmt"
"sync"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/hst"
. "git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/sys"
)

View File

@ -2,9 +2,9 @@ package setuid_test
import (
"git.gensokyo.uk/security/hakurei/acl"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/dbus"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/sandbox"
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
"git.gensokyo.uk/security/hakurei/system"

View File

@ -4,9 +4,9 @@ import (
"os"
"git.gensokyo.uk/security/hakurei/acl"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/dbus"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/sandbox"
"git.gensokyo.uk/security/hakurei/sandbox/seccomp"
"git.gensokyo.uk/security/hakurei/system"

View File

@ -7,7 +7,7 @@ import (
"os/user"
"strconv"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/hst"
)
// fs methods are not implemented using a real FS
@ -125,8 +125,8 @@ func (s *stubNixOS) Open(name string) (fs.File, error) {
}
}
func (s *stubNixOS) Paths() app.Paths {
return app.Paths{
func (s *stubNixOS) Paths() hst.Paths {
return hst.Paths{
SharePath: "/tmp/hakurei.1971",
RuntimePath: "/run/user/1971",
RunDirPath: "/run/user/1971/hakurei",

View File

@ -7,9 +7,9 @@ import (
"testing"
"time"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app/internal/setuid"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/app/internal/setuid"
"git.gensokyo.uk/security/hakurei/internal/sys"
"git.gensokyo.uk/security/hakurei/sandbox"
"git.gensokyo.uk/security/hakurei/system"

View File

@ -4,7 +4,7 @@ import (
"errors"
"log"
. "git.gensokyo.uk/security/hakurei/internal/app"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/hlog"
)

View File

@ -1,7 +1,7 @@
package setuid
import (
. "git.gensokyo.uk/security/hakurei/internal/app"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/sys"
"git.gensokyo.uk/security/hakurei/sandbox"
"git.gensokyo.uk/security/hakurei/system"

View File

@ -12,10 +12,10 @@ import (
"syscall"
"time"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/internal"
. "git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/sandbox"
"git.gensokyo.uk/security/hakurei/system"
)

View File

@ -17,11 +17,11 @@ import (
"syscall"
"git.gensokyo.uk/security/hakurei/acl"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app/instance/common"
"git.gensokyo.uk/security/hakurei/dbus"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal"
. "git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/app/instance/common"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/sys"
"git.gensokyo.uk/security/hakurei/sandbox"
@ -97,7 +97,7 @@ type shareHost struct {
runtimeSharePath string
seal *outcome
sc Paths
sc hst.Paths
}
// ensureRuntimeDir must be called if direct access to paths within XDG_RUNTIME_DIR is required
@ -183,7 +183,7 @@ func (seal *outcome) finalise(ctx context.Context, sys sys.State, config *hst.Co
if seal.user.username == "" {
seal.user.username = "chronos"
} else if !posixUsername.MatchString(seal.user.username) ||
len(seal.user.username) >= internal.Sysconf_SC_LOGIN_NAME_MAX() {
len(seal.user.username) >= internal.Sysconf(internal.SC_LOGIN_NAME_MAX) {
return hlog.WrapErr(ErrName,
fmt.Sprintf("invalid user name %q", seal.user.username))
}

View File

@ -104,7 +104,7 @@ func ShimMain() {
log.Fatalf("cannot receive shim setup params: %v", err)
} else {
internal.InstallFmsg(params.Verbose)
internal.InstallOutput(params.Verbose)
closeSetup = f
// the Go runtime does not expose siginfo_t so SIGCONT is handled in C to check si_pid

View File

@ -3,7 +3,7 @@ package setuid
import (
"strconv"
. "git.gensokyo.uk/security/hakurei/internal/app"
. "git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
)
func newInt(v int) *stringPair[int] { return &stringPair[int]{v, strconv.Itoa(v)} }

View File

@ -13,8 +13,8 @@ import (
"sync"
"syscall"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/hlog"
)

View File

@ -0,0 +1,9 @@
package state_test
import (
"testing"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
)
func TestMulti(t *testing.T) { testStore(t, state.NewMulti(t.TempDir())) }

View File

@ -5,8 +5,8 @@ import (
"io"
"time"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
)
var ErrNoConfig = errors.New("state does not contain config")

View File

@ -10,9 +10,9 @@ import (
"testing"
"time"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/state"
)
func testStore(t *testing.T, s state.Store) {

View File

@ -1,5 +1,8 @@
package main
// this works around go:embed '..' limitation
//go:generate cp ../../LICENSE .
import (
"context"
_ "embed"
@ -15,14 +18,14 @@ import (
"syscall"
"time"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app/instance"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/command"
"git.gensokyo.uk/security/hakurei/dbus"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/app/instance"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/internal/sys"
"git.gensokyo.uk/security/hakurei/sandbox"
"git.gensokyo.uk/security/hakurei/system"
@ -41,7 +44,7 @@ var std sys.State = new(sys.Std)
func main() {
// early init path, skips root check and duplicate PR_SET_DUMPABLE
sandbox.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallFmsg)
sandbox.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput)
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
log.Printf("cannot set SUID_DUMP_DISABLE: %s", err)
@ -67,7 +70,7 @@ func buildCommand(out io.Writer) command.Command {
flagVerbose bool
flagJSON bool
)
c := command.New(out, log.Printf, "hakurei", func([]string) error { internal.InstallFmsg(flagVerbose); return nil }).
c := command.New(out, log.Printf, "hakurei", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity").
Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable")

View File

@ -10,9 +10,9 @@ import (
"strings"
"syscall"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/state"
)
func tryPath(name string) (config *hst.Config) {

View File

@ -12,10 +12,10 @@ import (
"text/tabwriter"
"time"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/dbus"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/internal/state"
)
func printShowSystem(output io.Writer, short, flagJSON bool) {

View File

@ -5,10 +5,10 @@ import (
"testing"
"time"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/cmd/hakurei/internal/state"
"git.gensokyo.uk/security/hakurei/dbus"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/state"
)
var (

View File

@ -42,7 +42,7 @@ func main() {
flagVerbose bool
flagDropShell bool
)
c := command.New(os.Stderr, log.Printf, "planterette", func([]string) error { internal.InstallFmsg(flagVerbose); return nil }).
c := command.New(os.Stderr, log.Printf, "planterette", func([]string) error { internal.InstallOutput(flagVerbose); return nil }).
Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console").
Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action")

View File

@ -209,5 +209,5 @@ func TestHelperInit(t *testing.T) {
return
}
sandbox.SetOutput(hlog.Output{})
sandbox.Init(hlog.Prepare, internal.InstallFmsg)
sandbox.Init(hlog.Prepare, internal.InstallOutput)
}

View File

@ -53,5 +53,5 @@ func TestHelperInit(t *testing.T) {
return
}
sandbox.SetOutput(hlog.Output{})
sandbox.Init(hlog.Prepare, func(bool) { internal.InstallFmsg(false) })
sandbox.Init(hlog.Prepare, func(bool) { internal.InstallOutput(false) })
}

11
hst/paths.go Normal file
View File

@ -0,0 +1,11 @@
package hst
// Paths contains environment-dependent paths used by hakurei.
type Paths struct {
// path to shared directory (usually `/tmp/hakurei.%d`)
SharePath string `json:"share_path"`
// XDG_RUNTIME_DIR value (usually `/run/user/%d`)
RuntimePath string `json:"runtime_path"`
// application runtime directory (usually `/run/user/%d/hakurei`)
RunDirPath string `json:"run_dir_path"`
}

View File

@ -6,7 +6,7 @@ import (
"git.gensokyo.uk/security/hakurei/system"
)
func InstallFmsg(verbose bool) {
func InstallOutput(verbose bool) {
hlog.Store(verbose)
sandbox.SetOutput(hlog.Output{})
system.SetOutput(hlog.Output{})

View File

@ -1,11 +0,0 @@
package state_test
import (
"testing"
"git.gensokyo.uk/security/hakurei/internal/state"
)
func TestMulti(t *testing.T) {
testStore(t, state.NewMulti(t.TempDir()))
}

View File

@ -1,3 +1,4 @@
// Package sys wraps OS interaction library functions.
package sys
import (
@ -6,7 +7,7 @@ import (
"path"
"strconv"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal/hlog"
)
@ -40,15 +41,15 @@ type State interface {
Println(v ...any)
Printf(format string, v ...any)
// Paths returns a populated [Paths] struct.
Paths() app.Paths
// Paths returns a populated [hst.Paths] struct.
Paths() hst.Paths
// Uid invokes hsu and returns target uid.
// Any errors returned by Uid is already wrapped [fmsg.BaseError].
Uid(aid int) (int, error)
}
// CopyPaths is a generic implementation of [hst.Paths].
func CopyPaths(os State, v *app.Paths) {
func CopyPaths(os State, v *hst.Paths) {
v.SharePath = path.Join(os.TempDir(), "hakurei."+strconv.Itoa(os.Getuid()))
hlog.Verbosef("process share directory at %q", v.SharePath)

View File

@ -12,15 +12,15 @@ import (
"sync"
"syscall"
"git.gensokyo.uk/security/hakurei/hst"
"git.gensokyo.uk/security/hakurei/internal"
"git.gensokyo.uk/security/hakurei/internal/app"
"git.gensokyo.uk/security/hakurei/internal/hlog"
"git.gensokyo.uk/security/hakurei/sandbox"
)
// Std implements System using the standard library.
type Std struct {
paths app.Paths
paths hst.Paths
pathsOnce sync.Once
uidOnce sync.Once
@ -48,7 +48,7 @@ func (s *Std) Printf(format string, v ...any) { hlog.Verbosef(form
const xdgRuntimeDir = "XDG_RUNTIME_DIR"
func (s *Std) Paths() app.Paths {
func (s *Std) Paths() hst.Paths {
s.pathsOnce.Do(func() { CopyPaths(s, &s.paths) })
return s.paths
}

View File

@ -3,4 +3,6 @@ package internal
//#include <unistd.h>
import "C"
func Sysconf_SC_LOGIN_NAME_MAX() int { return int(C.sysconf(C._SC_LOGIN_NAME_MAX)) }
const SC_LOGIN_NAME_MAX = C._SC_LOGIN_NAME_MAX
func Sysconf(name C.int) int { return int(C.sysconf(name)) }

View File

@ -30,7 +30,7 @@ func TestContainer(t *testing.T) {
{
oldVerbose := hlog.Load()
oldOutput := sandbox.GetOutput()
internal.InstallFmsg(true)
internal.InstallOutput(true)
t.Cleanup(func() { hlog.Store(oldVerbose) })
t.Cleanup(func() { sandbox.SetOutput(oldOutput) })
}
@ -202,7 +202,7 @@ func TestHelperInit(t *testing.T) {
return
}
sandbox.SetOutput(hlog.Output{})
sandbox.Init(hlog.Prepare, internal.InstallFmsg)
sandbox.Init(hlog.Prepare, internal.InstallOutput)
}
func TestHelperCheckContainer(t *testing.T) {