aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app/shim
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-03-25 01:52:49 +0900
committerOphestra <cat@gensokyo.uk>2025-03-25 01:52:49 +0900
commit5c4058d5ac7e7944973ca8216c258fc50c194e22 (patch)
treec06597903233c75eca5a1edae8b06d93f0f81774 /internal/app/shim
parente732dca7629edb503df0c65df5f37a99600d567d (diff)
app: run in native sandbox
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/shim')
-rw-r--r--internal/app/shim/main.go129
-rw-r--r--internal/app/shim/payload.go23
-rw-r--r--internal/app/shim/proc.go (renamed from internal/app/shim/manager.go)35
3 files changed, 56 insertions, 131 deletions
diff --git a/internal/app/shim/main.go b/internal/app/shim/main.go
index 5634be4c..17f346a2 100644
--- a/internal/app/shim/main.go
+++ b/internal/app/shim/main.go
@@ -7,18 +7,26 @@ import (
"os"
"os/exec"
"os/signal"
- "path"
- "strconv"
"syscall"
+ "time"
- "git.gensokyo.uk/security/fortify/fst"
- "git.gensokyo.uk/security/fortify/helper"
"git.gensokyo.uk/security/fortify/internal"
- "git.gensokyo.uk/security/fortify/internal/app/init0"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/sandbox"
)
+const Env = "FORTIFY_SHIM"
+
+type Params struct {
+ // finalised container params
+ Container *sandbox.Params
+ // path to outer home directory
+ Home string
+
+ // verbosity pass through
+ Verbose bool
+}
+
// everything beyond this point runs as unconstrained target user
// proceed with caution!
@@ -27,17 +35,15 @@ func Main() {
// USE WITH CAUTION
fmsg.Prepare("shim")
- // setting this prevents ptrace
if err := sandbox.SetDumpable(sandbox.SUID_DUMP_DISABLE); err != nil {
log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err)
}
- // receive setup payload
var (
- payload Payload
+ params Params
closeSetup func() error
)
- if f, err := sandbox.Receive(Env, &payload, nil); err != nil {
+ if f, err := sandbox.Receive(Env, &params, nil); err != nil {
if errors.Is(err, sandbox.ErrInvalid) {
log.Fatal("invalid config descriptor")
}
@@ -45,32 +51,26 @@ func Main() {
log.Fatal("FORTIFY_SHIM not set")
}
- log.Fatalf("cannot decode shim setup payload: %v", err)
+ log.Fatalf("cannot receive shim setup params: %v", err)
} else {
- internal.InstallFmsg(payload.Verbose)
+ internal.InstallFmsg(params.Verbose)
closeSetup = f
}
- if payload.Bwrap == nil {
- log.Fatal("bwrap config not supplied")
- }
-
- // restore bwrap sync fd
- var syncFd *os.File
- if payload.Sync != nil {
- syncFd = os.NewFile(*payload.Sync, "sync")
+ if params.Container == nil || params.Container.Ops == nil {
+ log.Fatal("invalid container params")
}
// close setup socket
if err := closeSetup(); err != nil {
- log.Println("cannot close setup pipe:", err)
+ log.Printf("cannot close setup pipe: %v", err)
// not fatal
}
// ensure home directory as target user
- if s, err := os.Stat(payload.Home); err != nil {
+ if s, err := os.Stat(params.Home); err != nil {
if os.IsNotExist(err) {
- if err = os.Mkdir(payload.Home, 0700); err != nil {
+ if err = os.Mkdir(params.Home, 0700); err != nil {
log.Fatalf("cannot create home directory: %v", err)
}
} else {
@@ -79,72 +79,37 @@ func Main() {
// home directory is created, proceed
} else if !s.IsDir() {
- log.Fatalf("data path %q is not a directory", payload.Home)
+ log.Fatalf("path %q is not a directory", params.Home)
}
- var ic init0.Payload
-
- // resolve argv0
- ic.Argv = payload.Argv
- if len(ic.Argv) > 0 {
- // looked up from $PATH by parent
- ic.Argv0 = payload.Exec[1]
- } else {
- // no argv, look up shell instead
- var ok bool
- if payload.Bwrap.SetEnv == nil {
- log.Fatal("no command was specified and environment is unset")
- }
- if ic.Argv0, ok = payload.Bwrap.SetEnv["SHELL"]; !ok {
- log.Fatal("no command was specified and $SHELL was unset")
- }
-
- ic.Argv = []string{ic.Argv0}
+ var name string
+ if len(params.Container.Args) > 0 {
+ name = params.Container.Args[0]
}
-
- conf := payload.Bwrap
-
- var extraFiles []*os.File
-
- // serve setup payload
- if fd, encoder, err := sandbox.Setup(&extraFiles); err != nil {
- log.Fatalf("cannot pipe: %v", err)
- } else {
- conf.SetEnv[init0.Env] = strconv.Itoa(fd)
- go func() {
- fmsg.Verbose("transmitting config to init")
- if err = encoder.Encode(&ic); err != nil {
- log.Fatalf("cannot transmit init config: %v", err)
- }
- }()
- }
-
- helper.BubblewrapName = payload.Exec[0] // resolved bwrap path by parent
-
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop() // unreachable
- if b, err := helper.NewBwrap(
- ctx, path.Join(fst.Tmp, "sbin/init0"),
- nil, false,
- func(int, int) []string { return make([]string, 0) },
- func(cmd *exec.Cmd) { cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr },
- extraFiles,
- conf, syncFd,
- ); err != nil {
- log.Fatalf("malformed sandbox config: %v", err)
- } else {
- // run and pass through exit code
- if err = b.Start(); err != nil {
- log.Fatalf("cannot start target process: %v", err)
- } else if err = b.Wait(); err != nil {
- var exitError *exec.ExitError
- if !errors.As(err, &exitError) {
- log.Printf("wait: %v", err)
- internal.Exit(127)
- panic("unreachable")
+ container := sandbox.New(ctx, name)
+ container.Params = *params.Container
+ container.Stdin, container.Stdout, container.Stderr = os.Stdin, os.Stdout, os.Stderr
+ container.Cancel = func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) }
+ container.WaitDelay = 2 * time.Second
+
+ if err := container.Start(); err != nil {
+ fmsg.PrintBaseError(err, "cannot start container:")
+ os.Exit(1)
+ }
+ if err := container.Serve(); err != nil {
+ fmsg.PrintBaseError(err, "cannot configure container:")
+ }
+ if err := container.Wait(); err != nil {
+ var exitError *exec.ExitError
+ if !errors.As(err, &exitError) {
+ if errors.Is(err, context.Canceled) {
+ os.Exit(2)
}
- internal.Exit(exitError.ExitCode())
- panic("unreachable")
+ log.Printf("wait: %v", err)
+ os.Exit(127)
}
+ os.Exit(exitError.ExitCode())
}
}
diff --git a/internal/app/shim/payload.go b/internal/app/shim/payload.go
deleted file mode 100644
index e659e3fd..00000000
--- a/internal/app/shim/payload.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package shim
-
-import (
- "git.gensokyo.uk/security/fortify/helper/bwrap"
-)
-
-const Env = "FORTIFY_SHIM"
-
-type Payload struct {
- // child full argv
- Argv []string
- // bwrap, target full exec path
- Exec [2]string
- // bwrap config
- Bwrap *bwrap.Config
- // path to outer home directory
- Home string
- // sync fd
- Sync *uintptr
-
- // verbosity pass through
- Verbose bool
-}
diff --git a/internal/app/shim/manager.go b/internal/app/shim/proc.go
index 4ed75e53..8c73e697 100644
--- a/internal/app/shim/manager.go
+++ b/internal/app/shim/proc.go
@@ -8,9 +8,9 @@ import (
"os/exec"
"strconv"
"strings"
+ "syscall"
"time"
- "git.gensokyo.uk/security/fortify/helper/proc"
"git.gensokyo.uk/security/fortify/internal"
"git.gensokyo.uk/security/fortify/internal/fmsg"
"git.gensokyo.uk/security/fortify/sandbox"
@@ -25,10 +25,11 @@ type Shim struct {
killFallback chan error
// monitor to shim encoder
encoder *gob.Encoder
- // bwrap --sync-fd value
- sync *uintptr
}
+func (s *Shim) Unwrap() *exec.Cmd { return s.cmd }
+func (s *Shim) Fallback() chan error { return s.killFallback }
+
func (s *Shim) String() string {
if s.cmd == nil {
return "(unused shim manager)"
@@ -36,21 +37,9 @@ func (s *Shim) String() string {
return s.cmd.String()
}
-func (s *Shim) Unwrap() *exec.Cmd {
- return s.cmd
-}
-
-func (s *Shim) WaitFallback() chan error {
- return s.killFallback
-}
-
func (s *Shim) Start(
- // string representation of application id
aid string,
- // string representation of supplementary group ids
supp []string,
- // bwrap --sync-fd
- syncFd *os.File,
) (*time.Time, error) {
// prepare user switcher invocation
fsuPath := internal.MustFsuPath()
@@ -76,12 +65,6 @@ func (s *Shim) Start(
s.cmd.Stdin, s.cmd.Stdout, s.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
s.cmd.Dir = "/"
- // pass sync fd if set
- if syncFd != nil {
- fd := proc.ExtraFile(s.cmd, syncFd)
- s.sync = &fd
- }
-
fmsg.Verbose("starting shim via fsu:", s.cmd)
// withhold messages to stderr
fmsg.Suspend()
@@ -90,10 +73,11 @@ func (s *Shim) Start(
"cannot start fsu:")
}
startTime := time.Now().UTC()
+
return &startTime, nil
}
-func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
+func (s *Shim) Serve(ctx context.Context, params *Params) error {
// kill shim if something goes wrong and an error is returned
s.killFallback = make(chan error, 1)
killShim := func() {
@@ -103,9 +87,8 @@ func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
}
defer func() { killShim() }()
- payload.Sync = s.sync
encodeErr := make(chan error)
- go func() { encodeErr <- s.encoder.Encode(payload) }()
+ go func() { encodeErr <- s.encoder.Encode(params) }()
select {
// encode return indicates setup completion
@@ -121,11 +104,11 @@ func (s *Shim) Serve(ctx context.Context, payload *Payload) error {
case <-ctx.Done():
err := ctx.Err()
if errors.Is(err, context.Canceled) {
- return fmsg.WrapError(errors.New("shim setup canceled"),
+ return fmsg.WrapError(syscall.ECANCELED,
"shim setup canceled")
}
if errors.Is(err, context.DeadlineExceeded) {
- return fmsg.WrapError(errors.New("deadline exceeded waiting for shim"),
+ return fmsg.WrapError(syscall.ETIMEDOUT,
"deadline exceeded waiting for shim")
}
// unreachable