diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-02-26 19:46:43 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-02-26 19:51:44 +0900 |
| commit | 673b648bd35285a1dbfc0348716dd6bdd8064a51 (patch) | |
| tree | 7fa388c20fb7509060964e0380f744bf9178a006 /cmd | |
| parent | 45ad788c6d03bf368010fa8e2a39028bdf0fc078 (diff) | |
cmd/fpkg: call app in-process
Wrapping fortify is slow, painful and error-prone. Start apps in-process instead.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/fpkg/main.go | 47 | ||||
| -rw-r--r-- | cmd/fpkg/proc.go | 57 | ||||
| -rw-r--r-- | cmd/fpkg/with.go | 17 |
3 files changed, 65 insertions, 56 deletions
diff --git a/cmd/fpkg/main.go b/cmd/fpkg/main.go index ba0228f8..8075ba16 100644 --- a/cmd/fpkg/main.go +++ b/cmd/fpkg/main.go @@ -1,23 +1,32 @@ package main import ( + "context" "encoding/json" "errors" "log" "os" + "os/signal" "path" "syscall" "git.gensokyo.uk/security/fortify/command" "git.gensokyo.uk/security/fortify/fst" "git.gensokyo.uk/security/fortify/helper/bwrap" + "git.gensokyo.uk/security/fortify/helper/seccomp" + "git.gensokyo.uk/security/fortify/internal" + init0 "git.gensokyo.uk/security/fortify/internal/app/init" + "git.gensokyo.uk/security/fortify/internal/app/shim" "git.gensokyo.uk/security/fortify/internal/fmsg" + "git.gensokyo.uk/security/fortify/internal/sys" ) const shellPath = "/run/current-system/sw/bin/bash" var ( errSuccess = errors.New("success") + + std sys.State = new(sys.Std) ) func init() { @@ -28,14 +37,40 @@ func init() { } func main() { + // early init argv0 check, skips root check and duplicate PR_SET_DUMPABLE + init0.TryArgv0() + + if err := internal.PR_SET_DUMPABLE__SUID_DUMP_DISABLE(); err != nil { + log.Printf("cannot set SUID_DUMP_DISABLE: %s", err) + // not fatal: this program runs as the privileged user + } + + if os.Geteuid() == 0 { + log.Fatal("this program must not run as root") + } + + ctx, stop := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM) + defer stop() // unreachable + var ( flagVerbose bool flagDropShell bool ) - c := command.New(os.Stderr, log.Printf, "fpkg", func([]string) error { fmsg.Store(flagVerbose); return nil }). + c := command.New(os.Stderr, log.Printf, "fpkg", func([]string) error { + fmsg.Store(flagVerbose) + if flagVerbose { + seccomp.CPrintln = log.Println + } + 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 fortify action") + // internal commands + c.Command("shim", command.UsageInternal, func([]string) error { shim.Main(); return errSuccess }) + c.Command("init", command.UsageInternal, func([]string) error { init0.Main(); return errSuccess }) + { var ( flagDropShellActivate bool @@ -149,7 +184,7 @@ func main() { Setup steps for files owned by the target user. */ - withCacheDir("install", []string{ + withCacheDir(ctx, "install", []string{ // export inner bundle path in the environment "export BUNDLE=" + fst.Tmp + "/bundle", // replace inner /etc @@ -171,7 +206,7 @@ func main() { }, workDir, bundle, pathSet, flagDropShell, cleanup) if bundle.GPU { - withCacheDir("mesa-wrappers", []string{ + withCacheDir(ctx, "mesa-wrappers", []string{ // link nixGL mesa wrappers "mkdir -p nix/.nixGL", "ln -s " + bundle.Mesa + "/bin/nixGLIntel nix/.nixGL/nixGL", @@ -183,7 +218,7 @@ func main() { Activate home-manager generation. */ - withNixDaemon("activate", []string{ + withNixDaemon(ctx, "activate", []string{ // clean up broken links "mkdir -p .local/state/{nix,home-manager}", "chmod -R +w .local/state/{nix,home-manager}", @@ -251,7 +286,7 @@ func main() { */ if app.GPU && flagAutoDrivers { - withNixDaemon("nix-gl", []string{ + withNixDaemon(ctx, "nix-gl", []string{ "mkdir -p /nix/.nixGL/auto", "rm -rf /nix/.nixGL/auto", "export NIXPKGS_ALLOW_UNFREE=1", @@ -349,7 +384,7 @@ func main() { Spawn app. */ - fortifyApp(config, func() {}) + mustRunApp(ctx, config, func() {}) return errSuccess }). Flag(&flagDropShellNixGL, "s", command.BoolFlag(false), "Drop to a shell on nixGL build"). diff --git a/cmd/fpkg/proc.go b/cmd/fpkg/proc.go index 538c5ed4..ea106adf 100644 --- a/cmd/fpkg/proc.go +++ b/cmd/fpkg/proc.go @@ -1,59 +1,28 @@ package main import ( - "encoding/json" - "errors" - "io" - "log" + "context" "os" - "os/exec" "git.gensokyo.uk/security/fortify/fst" - "git.gensokyo.uk/security/fortify/internal" + "git.gensokyo.uk/security/fortify/internal/app" "git.gensokyo.uk/security/fortify/internal/fmsg" ) -func fortifyApp(config *fst.Config, beforeFail func()) { - var ( - cmd *exec.Cmd - st io.WriteCloser - ) - if p, ok := internal.Path(internal.Fortify); !ok { - beforeFail() - log.Fatal("invalid fortify path, this copy of fpkg is not compiled correctly") - } else if r, w, err := os.Pipe(); err != nil { - beforeFail() - log.Fatalf("cannot pipe: %v", err) +func mustRunApp(ctx context.Context, config *fst.Config, beforeFail func()) { + rs := new(fst.RunState) + a := app.MustNew(std) + + if sa, err := a.Seal(config); err != nil { + fmsg.PrintBaseError(err, "cannot seal app:") + rs.ExitCode = 1 } else { - if fmsg.Load() { - cmd = exec.Command(p, "-v", "app", "3") - } else { - cmd = exec.Command(p, "app", "3") - } - cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr - cmd.ExtraFiles = []*os.File{r} - st = w + // this updates ExitCode + app.PrintRunStateErr(rs, sa.Run(ctx, rs)) } - go func() { - if err := json.NewEncoder(st).Encode(config); err != nil { - beforeFail() - log.Fatalf("cannot send configuration: %v", err) - } - }() - - if err := cmd.Start(); err != nil { + if rs.ExitCode != 0 { beforeFail() - log.Fatalf("cannot start fortify: %v", err) - } - if err := cmd.Wait(); err != nil { - var exitError *exec.ExitError - if errors.As(err, &exitError) { - beforeFail() - internal.Exit(exitError.ExitCode()) - } else { - beforeFail() - log.Fatalf("cannot wait: %v", err) - } + os.Exit(rs.ExitCode) } } diff --git a/cmd/fpkg/with.go b/cmd/fpkg/with.go index b6928bd1..d7b0e968 100644 --- a/cmd/fpkg/with.go +++ b/cmd/fpkg/with.go @@ -1,6 +1,7 @@ package main import ( + "context" "path" "strings" @@ -10,10 +11,11 @@ import ( ) func withNixDaemon( + ctx context.Context, action string, command []string, net bool, updateConfig func(config *fst.Config) *fst.Config, app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func(), ) { - fortifyAppDropShell(updateConfig(&fst.Config{ + mustRunAppDropShell(ctx, updateConfig(&fst.Config{ ID: app.ID, Command: []string{shellPath, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " + // start nix-daemon @@ -56,8 +58,11 @@ func withNixDaemon( }), dropShell, beforeFail) } -func withCacheDir(action string, command []string, workDir string, app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { - fortifyAppDropShell(&fst.Config{ +func withCacheDir( + ctx context.Context, + action string, command []string, workDir string, + app *bundleInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { + mustRunAppDropShell(ctx, &fst.Config{ ID: app.ID, Command: []string{shellPath, "-lc", strings.Join(command, " && ")}, Confinement: fst.ConfinementConfig{ @@ -90,12 +95,12 @@ func withCacheDir(action string, command []string, workDir string, app *bundleIn }, dropShell, beforeFail) } -func fortifyAppDropShell(config *fst.Config, dropShell bool, beforeFail func()) { +func mustRunAppDropShell(ctx context.Context, config *fst.Config, dropShell bool, beforeFail func()) { if dropShell { config.Command = []string{shellPath, "-l"} - fortifyApp(config, beforeFail) + mustRunApp(ctx, config, beforeFail) beforeFail() internal.Exit(0) } - fortifyApp(config, beforeFail) + mustRunApp(ctx, config, beforeFail) } |
