package main import ( "encoding/json" "errors" "io" "os" "os/exec" "path" "git.gensokyo.uk/security/fortify/dbus" "git.gensokyo.uk/security/fortify/fst" "git.gensokyo.uk/security/fortify/internal" "git.gensokyo.uk/security/fortify/internal/fmsg" "git.gensokyo.uk/security/fortify/internal/system" ) type bundleInfo struct { Name string `json:"name"` Version string `json:"version"` // passed through to [fst.Config] ID string `json:"id"` // passed through to [fst.Config] AppID int `json:"app_id"` // passed through to [fst.Config] Groups []string `json:"groups,omitempty"` // passed through to [fst.Config] UserNS bool `json:"userns,omitempty"` // passed through to [fst.Config] Net bool `json:"net,omitempty"` // passed through to [fst.Config] Dev bool `json:"dev,omitempty"` // passed through to [fst.Config] NoNewSession bool `json:"no_new_session,omitempty"` // passed through to [fst.Config] MapRealUID bool `json:"map_real_uid,omitempty"` // passed through to [fst.Config] DirectWayland bool `json:"direct_wayland,omitempty"` // passed through to [fst.Config] SystemBus *dbus.Config `json:"system_bus,omitempty"` // passed through to [fst.Config] SessionBus *dbus.Config `json:"session_bus,omitempty"` // passed through to [fst.Config] Enablements system.Enablements `json:"enablements"` // allow gpu access within sandbox GPU bool `json:"gpu"` // inner nix store path to activate-and-exec script Launcher string `json:"launcher"` } func actionStart(args []string) { if len(args) < 1 { fmsg.Fatal("invalid arguments") } name := args[0] if !path.IsAbs(name) { if dir, err := os.Getwd(); err != nil { fmsg.Fatalf("cannot get current directory: %v", err) } else { name = path.Join(dir, name) } } bundle := new(bundleInfo) if f, err := os.Open(path.Join(name, "bundle.json")); err != nil { fmsg.Fatalf("cannot open bundle: %v", err) } else if err = json.NewDecoder(f).Decode(&bundle); err != nil { fmsg.Fatalf("cannot parse bundle metadata: %v", err) } else if err = f.Close(); err != nil { fmsg.Printf("cannot close bundle metadata: %v", err) } config := &fst.Config{ ID: bundle.ID, Command: append([]string{bundle.Launcher}, args[1:]...), Confinement: fst.ConfinementConfig{ AppID: bundle.AppID, Groups: bundle.Groups, Username: "fortify", Inner: path.Join("/data/data", bundle.ID), Outer: formatDataPath(bundle.ID), Sandbox: &fst.SandboxConfig{ Hostname: formatHostname(bundle.Name), UserNS: bundle.UserNS, Net: bundle.Net, Dev: bundle.Dev, NoNewSession: bundle.NoNewSession, MapRealUID: bundle.MapRealUID, DirectWayland: bundle.DirectWayland, Filesystem: []*fst.FilesystemConfig{ {Src: path.Join(name, "nix", "store"), Dst: "/nix/store", Must: true}, {Src: "/sys/block"}, {Src: "/sys/bus"}, {Src: "/sys/class"}, {Src: "/sys/dev"}, {Src: "/sys/devices"}, }, AutoEtc: true, }, SystemBus: bundle.SystemBus, SessionBus: bundle.SessionBus, Enablements: bundle.Enablements, }, } if bundle.GPU { config.Confinement.Sandbox.Filesystem = append(config.Confinement.Sandbox.Filesystem, &fst.FilesystemConfig{Src: "/dev/dri", Device: true}) } var ( cmd *exec.Cmd st io.WriteCloser ) if p, ok := internal.Check(internal.Fortify); !ok { fmsg.Fatal("invalid fortify path, this copy of flaunch is not compiled correctly") panic("unreachable") } else if r, w, err := os.Pipe(); err != nil { fmsg.Fatalf("cannot pipe: %v", err) panic("unreachable") } else { if fmsg.Verbose() { 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 } go func() { if err := json.NewEncoder(st).Encode(config); err != nil { fmsg.Fatalf("cannot send configuration: %v", err) } }() if err := cmd.Start(); err != nil { fmsg.Fatalf("cannot start fortify: %v", err) } if err := cmd.Wait(); err != nil { var exitError *exec.ExitError if errors.As(err, &exitError) { fmsg.Exit(exitError.ExitCode()) } else { fmsg.Fatalf("cannot wait: %v", err) } } fmsg.Exit(0) } func formatHostname(name string) string { if h, err := os.Hostname(); err != nil { fmsg.Printf("cannot get hostname: %v", err) return "fortify-" + name } else { return h + "-" + name } } func formatDataPath(id string) string { if p, ok := os.LookupEnv("FORTIFY_DATA_HOME"); ok { return path.Join(p, id) } else if p, ok = os.LookupEnv("HOME"); ok { return path.Join(p, ".app", id) } else { return path.Join("/var/lib/fortify/app", id) } }