aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/app/shim/main.go
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/main.go
parente732dca7629edb503df0c65df5f37a99600d567d (diff)
app: run in native sandbox
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/app/shim/main.go')
-rw-r--r--internal/app/shim/main.go129
1 files changed, 47 insertions, 82 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())
}
}