fortify: implement cleaner argument structure
All checks were successful
test / test (push) Successful in 18s
All checks were successful
test / test (push) Successful in 18s
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
69cc64ef56
commit
1c3c338905
135
config.go
135
config.go
@ -1,135 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/dbus"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/app"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/system"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
printTemplate bool
|
|
||||||
|
|
||||||
confPath string
|
|
||||||
|
|
||||||
dbusConfigSession string
|
|
||||||
dbusConfigSystem string
|
|
||||||
dbusID string
|
|
||||||
mpris bool
|
|
||||||
dbusVerbose bool
|
|
||||||
|
|
||||||
userName string
|
|
||||||
enablements [system.ELen]bool
|
|
||||||
|
|
||||||
launchMethodText string
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&printTemplate, "template", false, "Print a full config template and exit")
|
|
||||||
|
|
||||||
// config file, disables every other flag here
|
|
||||||
flag.StringVar(&confPath, "c", "nil", "Path to full app configuration, or \"nil\" to configure from flags")
|
|
||||||
|
|
||||||
flag.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults")
|
|
||||||
flag.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable")
|
|
||||||
flag.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available")
|
|
||||||
flag.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available")
|
|
||||||
flag.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy")
|
|
||||||
|
|
||||||
flag.StringVar(&userName, "u", "chronos", "Passwd name of user to run as")
|
|
||||||
flag.BoolVar(&enablements[system.EWayland], "wayland", false, "Share Wayland socket")
|
|
||||||
flag.BoolVar(&enablements[system.EX11], "X", false, "Share X11 socket and allow connection")
|
|
||||||
flag.BoolVar(&enablements[system.EDBus], "dbus", false, "Proxy D-Bus connection")
|
|
||||||
flag.BoolVar(&enablements[system.EPulse], "pulse", false, "Share PulseAudio socket and cookie")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
methodHelpString := "Method of launching the child process, can be one of \"sudo\""
|
|
||||||
if os.SdBooted() {
|
|
||||||
methodHelpString += ", \"systemd\""
|
|
||||||
}
|
|
||||||
|
|
||||||
flag.StringVar(&launchMethodText, "method", "sudo", methodHelpString)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryTemplate() {
|
|
||||||
if printTemplate {
|
|
||||||
if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil {
|
|
||||||
fmsg.Fatalf("cannot generate template: %v", err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else {
|
|
||||||
fmt.Println(string(s))
|
|
||||||
}
|
|
||||||
fmsg.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadConfig() *app.Config {
|
|
||||||
if confPath == "nil" {
|
|
||||||
// config from flags
|
|
||||||
return configFromFlags()
|
|
||||||
} else {
|
|
||||||
// config from file
|
|
||||||
c := new(app.Config)
|
|
||||||
if f, err := os.Open(confPath); err != nil {
|
|
||||||
fmsg.Fatalf("cannot access config file %q: %s", confPath, err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else if err = json.NewDecoder(f).Decode(&c); err != nil {
|
|
||||||
fmsg.Fatalf("cannot parse config file %q: %s", confPath, err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func configFromFlags() (config *app.Config) {
|
|
||||||
// initialise config from flags
|
|
||||||
config = &app.Config{
|
|
||||||
ID: dbusID,
|
|
||||||
User: userName,
|
|
||||||
Command: flag.Args(),
|
|
||||||
Method: launchMethodText,
|
|
||||||
}
|
|
||||||
|
|
||||||
// enablements from flags
|
|
||||||
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
|
||||||
if enablements[i] {
|
|
||||||
config.Confinement.Enablements.Set(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse D-Bus config file from flags if applicable
|
|
||||||
if enablements[system.EDBus] {
|
|
||||||
if dbusConfigSession == "builtin" {
|
|
||||||
config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris)
|
|
||||||
} else {
|
|
||||||
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
|
||||||
fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
|
||||||
} else {
|
|
||||||
config.Confinement.SessionBus = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// system bus proxy is optional
|
|
||||||
if dbusConfigSystem != "nil" {
|
|
||||||
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
|
||||||
fmsg.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
|
||||||
} else {
|
|
||||||
config.Confinement.SystemBus = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// override log from configuration
|
|
||||||
if dbusVerbose {
|
|
||||||
config.Confinement.SessionBus.Log = true
|
|
||||||
config.Confinement.SystemBus.Log = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
25
license.go
25
license.go
@ -1,25 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed LICENSE
|
|
||||||
license string
|
|
||||||
|
|
||||||
printLicense bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&printLicense, "license", false, "Print license")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryLicense() {
|
|
||||||
if printLicense {
|
|
||||||
fmt.Println(license)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
197
main.go
197
main.go
@ -1,16 +1,26 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"git.ophivana.moe/security/fortify/dbus"
|
||||||
"git.ophivana.moe/security/fortify/internal"
|
"git.ophivana.moe/security/fortify/internal"
|
||||||
"git.ophivana.moe/security/fortify/internal/app"
|
"git.ophivana.moe/security/fortify/internal/app"
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
"git.ophivana.moe/security/fortify/internal/fmsg"
|
||||||
"git.ophivana.moe/security/fortify/internal/linux"
|
"git.ophivana.moe/security/fortify/internal/linux"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/state"
|
||||||
|
"git.ophivana.moe/security/fortify/internal/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagVerbose bool
|
flagVerbose bool
|
||||||
|
|
||||||
|
//go:embed LICENSE
|
||||||
|
license string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -28,29 +38,193 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
fmsg.SetVerbose(flagVerbose)
|
fmsg.SetVerbose(flagVerbose)
|
||||||
|
|
||||||
if os.SdBooted() {
|
|
||||||
fmsg.VPrintln("system booted with systemd as init system")
|
|
||||||
}
|
|
||||||
|
|
||||||
// root check
|
// root check
|
||||||
if os.Geteuid() == 0 {
|
if os.Geteuid() == 0 {
|
||||||
fmsg.Fatal("this program must not run as root")
|
fmsg.Fatal("this program must not run as root")
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// version/license/template command early exit
|
printHelp := func() {
|
||||||
tryVersion()
|
fmt.Println()
|
||||||
tryLicense()
|
fmt.Println("Usage:\tfortify [-v] COMMAND [OPTIONS]")
|
||||||
tryTemplate()
|
fmt.Println()
|
||||||
|
fmt.Println("Commands:")
|
||||||
|
w := tabwriter.NewWriter(os.Stdout(), 0, 1, 4, ' ', 0)
|
||||||
|
commands := [][2]string{
|
||||||
|
{"app", "Launch app from specified config file"},
|
||||||
|
{"run", "Start a permissive default sandbox"},
|
||||||
|
{"ps", "Show state information of active apps"},
|
||||||
|
{"version", "Show fortify version"},
|
||||||
|
{"license", "Show full license text"},
|
||||||
|
{"template", "Product a full config template"},
|
||||||
|
{"help", "Show this help message"},
|
||||||
|
}
|
||||||
|
for _, c := range commands {
|
||||||
|
_, _ = fmt.Fprintf(w, "\t%s\t%s\n", c[0], c[1])
|
||||||
|
}
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
fmsg.Fatalf("cannot print help: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
// state query command early exit
|
args := flag.Args()
|
||||||
tryState()
|
if len(args) == 0 {
|
||||||
|
printHelp()
|
||||||
|
fmsg.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch args[0] {
|
||||||
|
case "version": // print version string
|
||||||
|
if v, ok := internal.Check(internal.Version); ok {
|
||||||
|
fmt.Println(v)
|
||||||
|
} else {
|
||||||
|
fmt.Println("impure")
|
||||||
|
}
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "license": // print embedded license
|
||||||
|
fmt.Println(license)
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "template": // print full template configuration
|
||||||
|
if s, err := json.MarshalIndent(app.Template(), "", " "); err != nil {
|
||||||
|
fmsg.Fatalf("cannot generate template: %v", err)
|
||||||
|
panic("unreachable")
|
||||||
|
} else {
|
||||||
|
fmt.Println(string(s))
|
||||||
|
}
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "help": // print help message
|
||||||
|
printHelp()
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "ps": // print all state info
|
||||||
|
var w *tabwriter.Writer
|
||||||
|
state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath)
|
||||||
|
if w != nil {
|
||||||
|
if err := w.Flush(); err != nil {
|
||||||
|
fmsg.Println("cannot format output:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("No information available")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmsg.Exit(0)
|
||||||
|
case "app": // launch app from configuration file
|
||||||
|
if len(args) < 2 {
|
||||||
|
fmsg.Fatal("app requires at least 1 argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := new(app.Config)
|
||||||
|
if f, err := os.Open(args[1]); err != nil {
|
||||||
|
fmsg.Fatalf("cannot access config file %q: %s", args[1], err)
|
||||||
|
panic("unreachable")
|
||||||
|
} else if err = json.NewDecoder(f).Decode(&config); err != nil {
|
||||||
|
fmsg.Fatalf("cannot parse config file %q: %s", args[1], err)
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
// append extra args
|
||||||
|
config.Command = append(config.Command, args[2:]...)
|
||||||
|
|
||||||
// invoke app
|
// invoke app
|
||||||
|
runApp(config)
|
||||||
|
case "run": // run app in permissive defaults usage pattern
|
||||||
|
set := flag.NewFlagSet("run", flag.ExitOnError)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbusConfigSession string
|
||||||
|
dbusConfigSystem string
|
||||||
|
dbusID string
|
||||||
|
mpris bool
|
||||||
|
dbusVerbose bool
|
||||||
|
|
||||||
|
userName string
|
||||||
|
enablements [system.ELen]bool
|
||||||
|
|
||||||
|
launchMethodText string
|
||||||
|
)
|
||||||
|
|
||||||
|
set.StringVar(&dbusConfigSession, "dbus-config", "builtin", "Path to D-Bus proxy config file, or \"builtin\" for defaults")
|
||||||
|
set.StringVar(&dbusConfigSystem, "dbus-system", "nil", "Path to system D-Bus proxy config file, or \"nil\" to disable")
|
||||||
|
set.StringVar(&dbusID, "dbus-id", "", "D-Bus ID of application, leave empty to disable own paths, has no effect if custom config is available")
|
||||||
|
set.BoolVar(&mpris, "mpris", false, "Allow owning MPRIS D-Bus path, has no effect if custom config is available")
|
||||||
|
set.BoolVar(&dbusVerbose, "dbus-log", false, "Force logging in the D-Bus proxy")
|
||||||
|
|
||||||
|
set.StringVar(&userName, "u", "chronos", "Passwd name of user to run as")
|
||||||
|
set.BoolVar(&enablements[system.EWayland], "wayland", false, "Share Wayland socket")
|
||||||
|
set.BoolVar(&enablements[system.EX11], "X", false, "Share X11 socket and allow connection")
|
||||||
|
set.BoolVar(&enablements[system.EDBus], "dbus", false, "Proxy D-Bus connection")
|
||||||
|
set.BoolVar(&enablements[system.EPulse], "pulse", false, "Share PulseAudio socket and cookie")
|
||||||
|
|
||||||
|
methodHelpString := "Method of launching the child process, can be one of \"sudo\""
|
||||||
|
if os.SdBooted() {
|
||||||
|
methodHelpString += ", \"systemd\""
|
||||||
|
}
|
||||||
|
set.StringVar(&launchMethodText, "method", "sudo", methodHelpString)
|
||||||
|
|
||||||
|
// Ignore errors; set is set for ExitOnError.
|
||||||
|
_ = set.Parse(args[1:])
|
||||||
|
|
||||||
|
// initialise config from flags
|
||||||
|
config := &app.Config{
|
||||||
|
ID: dbusID,
|
||||||
|
User: userName,
|
||||||
|
Command: set.Args(),
|
||||||
|
Method: launchMethodText,
|
||||||
|
}
|
||||||
|
|
||||||
|
// enablements from flags
|
||||||
|
for i := system.Enablement(0); i < system.Enablement(system.ELen); i++ {
|
||||||
|
if enablements[i] {
|
||||||
|
config.Confinement.Enablements.Set(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse D-Bus config file from flags if applicable
|
||||||
|
if enablements[system.EDBus] {
|
||||||
|
if dbusConfigSession == "builtin" {
|
||||||
|
config.Confinement.SessionBus = dbus.NewConfig(dbusID, true, mpris)
|
||||||
|
} else {
|
||||||
|
if c, err := dbus.NewConfigFromFile(dbusConfigSession); err != nil {
|
||||||
|
fmsg.Fatalf("cannot load session bus proxy config from %q: %s", dbusConfigSession, err)
|
||||||
|
} else {
|
||||||
|
config.Confinement.SessionBus = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// system bus proxy is optional
|
||||||
|
if dbusConfigSystem != "nil" {
|
||||||
|
if c, err := dbus.NewConfigFromFile(dbusConfigSystem); err != nil {
|
||||||
|
fmsg.Fatalf("cannot load system bus proxy config from %q: %s", dbusConfigSystem, err)
|
||||||
|
} else {
|
||||||
|
config.Confinement.SystemBus = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// override log from configuration
|
||||||
|
if dbusVerbose {
|
||||||
|
config.Confinement.SessionBus.Log = true
|
||||||
|
config.Confinement.SystemBus.Log = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoke app
|
||||||
|
runApp(config)
|
||||||
|
default:
|
||||||
|
fmsg.Fatalf("%q is not a valid command", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func runApp(config *app.Config) {
|
||||||
|
if os.SdBooted() {
|
||||||
|
fmsg.VPrintln("system booted with systemd as init system")
|
||||||
|
}
|
||||||
|
|
||||||
a, err := app.New(os)
|
a, err := app.New(os)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmsg.Fatalf("cannot create app: %s\n", err)
|
fmsg.Fatalf("cannot create app: %s\n", err)
|
||||||
} else if err = a.Seal(loadConfig()); err != nil {
|
} else if err = a.Seal(config); err != nil {
|
||||||
logBaseError(err, "cannot seal app:")
|
logBaseError(err, "cannot seal app:")
|
||||||
fmsg.Exit(1)
|
fmsg.Exit(1)
|
||||||
} else if err = a.Start(); err != nil {
|
} else if err = a.Start(); err != nil {
|
||||||
@ -69,4 +243,5 @@ func main() {
|
|||||||
fmsg.Println("inner wait failed:", err)
|
fmsg.Println("inner wait failed:", err)
|
||||||
}
|
}
|
||||||
fmsg.Exit(r)
|
fmsg.Exit(r)
|
||||||
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
35
state.go
35
state.go
@ -1,35 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/internal/fmsg"
|
|
||||||
"git.ophivana.moe/security/fortify/internal/state"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
stateActionEarly bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&stateActionEarly, "state", false, "print state information of active launchers")
|
|
||||||
}
|
|
||||||
|
|
||||||
// tryState is called after app initialisation
|
|
||||||
func tryState() {
|
|
||||||
if stateActionEarly {
|
|
||||||
var w *tabwriter.Writer
|
|
||||||
state.MustPrintLauncherStateSimpleGlobal(&w, os.Paths().RunDirPath)
|
|
||||||
if w != nil {
|
|
||||||
if err := w.Flush(); err != nil {
|
|
||||||
fmsg.Println("cannot format output:", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Println("No information available")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmsg.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
27
version.go
27
version.go
@ -1,27 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.ophivana.moe/security/fortify/internal"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
printVersion bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
flag.BoolVar(&printVersion, "V", false, "Print version")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryVersion() {
|
|
||||||
if printVersion {
|
|
||||||
if v, ok := internal.Check(internal.Version); ok {
|
|
||||||
fmt.Println(v)
|
|
||||||
} else {
|
|
||||||
fmt.Println("impure")
|
|
||||||
}
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user