diff options
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/hakurei/command.go | 132 | ||||
| -rw-r--r-- | cmd/hakurei/print.go | 31 | ||||
| -rw-r--r-- | cmd/hakurei/print_test.go | 113 | ||||
| -rw-r--r-- | cmd/hpkg/app.go | 14 | ||||
| -rw-r--r-- | cmd/hpkg/test/test.py | 6 | ||||
| -rw-r--r-- | cmd/hpkg/with.go | 50 |
6 files changed, 217 insertions, 129 deletions
diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index d284e075..45755521 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -2,10 +2,12 @@ package main import ( "context" + "errors" "fmt" "io" "log" "os" + "os/exec" "os/user" "strconv" "sync" @@ -52,7 +54,9 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE // config extraArgs... config := tryPath(msg, args[0]) - config.Args = append(config.Args, args[1:]...) + if config != nil && config.Container != nil { + config.Container.Args = append(config.Container.Args, args[1:]...) + } app.Main(ctx, msg, config) panic("unreachable") @@ -75,12 +79,6 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE ) c.NewCommand("run", "Configure and start a permissive container", func(args []string) error { - // initialise config from flags - config := &hst.Config{ - ID: flagID, - Args: args, - } - if flagIdentity < hst.IdentityMin || flagIdentity > hst.IdentityMax { log.Fatalf("identity %d out of range", flagIdentity) } @@ -106,41 +104,109 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE } ) - if flagHomeDir == "os" { - passwdOnce.Do(passwdFunc) - flagHomeDir = passwd.HomeDir - } - - if flagUserName == "chronos" { - passwdOnce.Do(passwdFunc) - flagUserName = passwd.Username + // paths are identical, resolve inner shell and program path + shell := container.AbsFHSRoot.Append("bin", "sh") + if a, err := container.NewAbs(os.Getenv("SHELL")); err == nil { + shell = a } - - config.Identity = flagIdentity - config.Groups = flagGroups - config.Username = flagUserName - - if a, err := container.NewAbs(flagHomeDir); err != nil { - log.Fatal(err.Error()) - return err - } else { - config.Home = a + progPath := shell + if len(args) > 0 { + if p, err := exec.LookPath(args[0]); err != nil { + log.Fatal(errors.Unwrap(err)) + return err + } else if progPath, err = container.NewAbs(p); err != nil { + log.Fatal(err.Error()) + return err + } } - var e hst.Enablement + var et hst.Enablement if flagWayland { - e |= hst.EWayland + et |= hst.EWayland } if flagX11 { - e |= hst.EX11 + et |= hst.EX11 } if flagDBus { - e |= hst.EDBus + et |= hst.EDBus } if flagPulse { - e |= hst.EPulse + et |= hst.EPulse + } + + config := &hst.Config{ + ID: flagID, + Identity: flagIdentity, + Groups: flagGroups, + Enablements: hst.NewEnablements(et), + + Container: &hst.ContainerConfig{ + Userns: true, + HostNet: true, + Tty: true, + HostAbstract: true, + + Filesystem: []hst.FilesystemConfigJSON{ + // autoroot, includes the home directory + {FilesystemConfig: &hst.FSBind{ + Target: container.AbsFHSRoot, + Source: container.AbsFHSRoot, + Write: true, + Special: true, + }}, + }, + + Username: flagUserName, + Shell: shell, + + Path: progPath, + Args: args, + }, + } + + // bind GPU stuff + if et&(hst.EX11|hst.EWayland) != 0 { + config.Container.Filesystem = append(config.Container.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{ + Source: container.AbsFHSDev.Append("dri"), + Device: true, + Optional: true, + }}) + } + + config.Container.Filesystem = append(config.Container.Filesystem, + // opportunistically bind kvm + hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{ + Source: container.AbsFHSDev.Append("kvm"), + Device: true, + Optional: true, + }}, + + // do autoetc last + hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{ + Target: container.AbsFHSEtc, + Source: container.AbsFHSEtc, + Special: true, + }}, + ) + + if config.Container.Username == "chronos" { + passwdOnce.Do(passwdFunc) + config.Container.Username = passwd.Username + } + + { + homeDir := flagHomeDir + if homeDir == "os" { + passwdOnce.Do(passwdFunc) + homeDir = passwd.HomeDir + } + if a, err := container.NewAbs(homeDir); err != nil { + log.Fatal(err.Error()) + return err + } else { + config.Container.Home = a + } } - config.Enablements = hst.NewEnablements(e) // parse D-Bus config file from flags if applicable if flagDBus { @@ -218,7 +284,9 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE if config == nil { config = tryPath(msg, name) } - printShowInstance(os.Stdout, time.Now().UTC(), entry, config, flagShort, flagJSON) + if !printShowInstance(os.Stdout, time.Now().UTC(), entry, config, flagShort, flagJSON) { + os.Exit(1) + } default: log.Fatal("show requires 1 argument") diff --git a/cmd/hakurei/print.go b/cmd/hakurei/print.go index eefe1345..c9084a58 100644 --- a/cmd/hakurei/print.go +++ b/cmd/hakurei/print.go @@ -11,6 +11,7 @@ import ( "text/tabwriter" "time" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/app" "hakurei.app/internal/app/state" @@ -39,7 +40,9 @@ func printShowSystem(output io.Writer, short, flagJSON bool) { func printShowInstance( output io.Writer, now time.Time, instance *state.State, config *hst.Config, - short, flagJSON bool) { + short, flagJSON bool) (valid bool) { + valid = true + if flagJSON { if instance != nil { printJSON(output, short, instance) @@ -52,8 +55,11 @@ func printShowInstance( t := newPrinter(output) defer t.MustFlush() - if config.Container == nil { - mustPrint(output, "Warning: this configuration uses permissive defaults!\n\n") + if err := config.Validate(); err != nil { + valid = false + if m, ok := container.GetErrorMessage(err); ok { + mustPrint(output, "Error: "+m+"!\n\n") + } } if instance != nil { @@ -73,11 +79,11 @@ func printShowInstance( if len(config.Groups) > 0 { t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", ")) } - if config.Home != nil { - t.Printf(" Home:\t%s\n", config.Home) - } if config.Container != nil { params := config.Container + if params.Home != nil { + t.Printf(" Home:\t%s\n", params.Home) + } if params.Hostname != "" { t.Printf(" Hostname:\t%s\n", params.Hostname) } @@ -100,12 +106,12 @@ func printShowInstance( } t.Printf(" Flags:\t%s\n", strings.Join(flags, " ")) - if config.Path != nil { - t.Printf(" Path:\t%s\n", config.Path) + if params.Path != nil { + t.Printf(" Path:\t%s\n", params.Path) + } + if len(params.Args) > 0 { + t.Printf(" Arguments:\t%s\n", strings.Join(params.Args, " ")) } - } - if len(config.Args) > 0 { - t.Printf(" Arguments:\t%s\n", strings.Join(config.Args, " ")) } t.Printf("\n") @@ -114,6 +120,7 @@ func printShowInstance( t.Printf("Filesystem\n") for _, f := range config.Container.Filesystem { if !f.Valid() { + valid = false t.Println(" <invalid>") continue } @@ -161,6 +168,8 @@ func printShowInstance( printDBus(config.SystemBus) t.Printf("\n") } + + return } func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) { diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go index 49579a82..a666d251 100644 --- a/cmd/hakurei/print_test.go +++ b/cmd/hakurei/print_test.go @@ -27,13 +27,14 @@ var ( testAppTime = time.Unix(0, 9).UTC() ) -func Test_printShowInstance(t *testing.T) { +func TestPrintShowInstance(t *testing.T) { testCases := []struct { name string instance *state.State config *hst.Config short, json bool want string + valid bool }{ {"config", nil, hst.Template(), false, false, `App Identity: 9 (org.chromium.Chromium) @@ -71,21 +72,25 @@ System bus Filter: true Talk: ["org.bluez" "org.freedesktop.Avahi" "org.freedesktop.UPower"] -`}, - {"config pd", nil, new(hst.Config), false, false, `Warning: this configuration uses permissive defaults! +`, true}, + {"config pd", nil, new(hst.Config), false, false, `Error: configuration missing container state! App Identity: 0 Enablements: (no enablements) -`}, - {"config flag none", nil, &hst.Config{Container: new(hst.ContainerConfig)}, false, false, `App +`, false}, + {"config flag none", nil, &hst.Config{Container: new(hst.ContainerConfig)}, false, false, `Error: container configuration missing path to home directory! + +App Identity: 0 Enablements: (no enablements) Flags: none -`}, - {"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]*hst.ExtraPermConfig, 1)}, false, false, `App +`, false}, + {"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]*hst.ExtraPermConfig, 1)}, false, false, `Error: container configuration missing path to home directory! + +App Identity: 0 Enablements: (no enablements) Flags: none @@ -95,8 +100,8 @@ Filesystem Extra ACL -`}, - {"config pd dbus see", nil, &hst.Config{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}, false, false, `Warning: this configuration uses permissive defaults! +`, false}, + {"config pd dbus see", nil, &hst.Config{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}, false, false, `Error: configuration missing container state! App Identity: 0 @@ -106,7 +111,7 @@ Session bus Filter: false See: ["org.example.test"] -`}, +`, false}, {"instance", testState, hst.Template(), false, false, `State Instance: 8e2c76b066dabe574cf073bdb46eb5c1 (3735928559) @@ -148,8 +153,8 @@ System bus Filter: true Talk: ["org.bluez" "org.freedesktop.Avahi" "org.freedesktop.UPower"] -`}, - {"instance pd", testState, new(hst.Config), false, false, `Warning: this configuration uses permissive defaults! +`, true}, + {"instance pd", testState, new(hst.Config), false, false, `Error: configuration missing container state! State Instance: 8e2c76b066dabe574cf073bdb46eb5c1 (3735928559) @@ -159,10 +164,10 @@ App Identity: 0 Enablements: (no enablements) -`}, +`, false}, {"json nil", nil, nil, false, true, `null -`}, +`, true}, {"json instance", testState, nil, false, true, `{ "instance": [ 142, @@ -185,14 +190,6 @@ App "pid": 3735928559, "config": { "id": "org.chromium.Chromium", - "path": "/run/current-system/sw/bin/chromium", - "args": [ - "chromium", - "--ignore-gpu-blocklist", - "--disable-smooth-scrolling", - "--enable-features=UseOzonePlatform", - "--ozone-platform=wayland" - ], "enablements": { "wayland": true, "dbus": true, @@ -234,9 +231,6 @@ App "broadcast": null, "filter": true }, - "username": "chronos", - "shell": "/run/current-system/sw/bin/zsh", - "home": "/data/data/org.chromium.Chromium", "extra_perms": [ { "ensure": true, @@ -331,22 +325,25 @@ App "dev": true, "optional": true } + ], + "username": "chronos", + "shell": "/run/current-system/sw/bin/zsh", + "home": "/data/data/org.chromium.Chromium", + "path": "/run/current-system/sw/bin/chromium", + "args": [ + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland" ] } }, "time": "1970-01-01T00:00:00.000000009Z" } -`}, +`, true}, {"json config", nil, hst.Template(), false, true, `{ "id": "org.chromium.Chromium", - "path": "/run/current-system/sw/bin/chromium", - "args": [ - "chromium", - "--ignore-gpu-blocklist", - "--disable-smooth-scrolling", - "--enable-features=UseOzonePlatform", - "--ozone-platform=wayland" - ], "enablements": { "wayland": true, "dbus": true, @@ -388,9 +385,6 @@ App "broadcast": null, "filter": true }, - "username": "chronos", - "shell": "/run/current-system/sw/bin/zsh", - "home": "/data/data/org.chromium.Chromium", "extra_perms": [ { "ensure": true, @@ -485,26 +479,39 @@ App "dev": true, "optional": true } + ], + "username": "chronos", + "shell": "/run/current-system/sw/bin/zsh", + "home": "/data/data/org.chromium.Chromium", + "path": "/run/current-system/sw/bin/chromium", + "args": [ + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland" ] } } -`}, +`, true}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { output := new(strings.Builder) - printShowInstance(output, testTime, tc.instance, tc.config, tc.short, tc.json) + gotValid := printShowInstance(output, testTime, tc.instance, tc.config, tc.short, tc.json) if got := output.String(); got != tc.want { - t.Errorf("printShowInstance: got\n%s\nwant\n%s", - got, tc.want) + t.Errorf("printShowInstance: \n%s\nwant\n%s", got, tc.want) return } + if gotValid != tc.valid { + t.Errorf("printShowInstance: valid = %v, want %v", gotValid, tc.valid) + } }) } } -func Test_printPs(t *testing.T) { +func TestPrintPs(t *testing.T) { testCases := []struct { name string entries state.Entries @@ -547,14 +554,6 @@ func Test_printPs(t *testing.T) { "pid": 3735928559, "config": { "id": "org.chromium.Chromium", - "path": "/run/current-system/sw/bin/chromium", - "args": [ - "chromium", - "--ignore-gpu-blocklist", - "--disable-smooth-scrolling", - "--enable-features=UseOzonePlatform", - "--ozone-platform=wayland" - ], "enablements": { "wayland": true, "dbus": true, @@ -596,9 +595,6 @@ func Test_printPs(t *testing.T) { "broadcast": null, "filter": true }, - "username": "chronos", - "shell": "/run/current-system/sw/bin/zsh", - "home": "/data/data/org.chromium.Chromium", "extra_perms": [ { "ensure": true, @@ -693,6 +689,17 @@ func Test_printPs(t *testing.T) { "dev": true, "optional": true } + ], + "username": "chronos", + "shell": "/run/current-system/sw/bin/zsh", + "home": "/data/data/org.chromium.Chromium", + "path": "/run/current-system/sw/bin/chromium", + "args": [ + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland" ] } }, diff --git a/cmd/hpkg/app.go b/cmd/hpkg/app.go index 1a1437ff..a4910aae 100644 --- a/cmd/hpkg/app.go +++ b/cmd/hpkg/app.go @@ -66,19 +66,12 @@ func (app *appInfo) toHst(pathSet *appPathSet, pathname *container.Absolute, arg config := &hst.Config{ ID: app.ID, - Path: pathname, - Args: argv, - Enablements: app.Enablements, SystemBus: app.SystemBus, SessionBus: app.SessionBus, DirectWayland: app.DirectWayland, - Username: "hakurei", - Shell: pathShell, - Home: pathDataData.Append(app.ID), - Identity: app.Identity, Groups: app.Groups, @@ -107,6 +100,13 @@ func (app *appInfo) toHst(pathSet *appPathSet, pathname *container.Absolute, arg {FilesystemConfig: &hst.FSBind{Source: container.AbsFHSSys.Append("devices"), Optional: true}}, {FilesystemConfig: &hst.FSBind{Target: pathDataData.Append(app.ID), Source: pathSet.homeDir, Write: true, Ensure: true}}, }, + + Username: "hakurei", + Shell: pathShell, + Home: pathDataData.Append(app.ID), + + Path: pathname, + Args: argv, }, ExtraPerms: []*hst.ExtraPermConfig{ {Path: dataHome, Execute: true}, diff --git a/cmd/hpkg/test/test.py b/cmd/hpkg/test/test.py index d491def3..e2a002d6 100644 --- a/cmd/hpkg/test/test.py +++ b/cmd/hpkg/test/test.py @@ -62,11 +62,11 @@ def check_state(name, enablements): config = instance['config'] - if len(config['args']) != 1 or not (config['args'][0].startswith("/nix/store/")) or f"hakurei-{name}-" not in (config['args'][0]): - raise Exception(f"unexpected args {instance['config']['args']}") + if len(config['container']['args']) != 1 or not (config['container']['args'][0].startswith("/nix/store/")) or f"hakurei-{name}-" not in (config['container']['args'][0]): + raise Exception(f"unexpected args {config['container']['args']}") if config['enablements'] != enablements: - raise Exception(f"unexpected enablements {instance['config']['enablements']}") + raise Exception(f"unexpected enablements {config['enablements']}") start_all() diff --git a/cmd/hpkg/with.go b/cmd/hpkg/with.go index 59545c62..b3ccd47a 100644 --- a/cmd/hpkg/with.go +++ b/cmd/hpkg/with.go @@ -18,22 +18,6 @@ func withNixDaemon( mustRunAppDropShell(ctx, msg, updateConfig(&hst.Config{ ID: app.ID, - Path: pathShell, - Args: []string{bash, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " + - // start nix-daemon - "nix-daemon --store / & " + - // wait for socket to appear - "(while [ ! -S /nix/var/nix/daemon-socket/socket ]; do sleep 0.01; done) && " + - // create directory so nix stops complaining - "mkdir -p /nix/var/nix/profiles/per-user/root/channels && " + - strings.Join(command, " && ") + - // terminate nix-daemon - " && pkill nix-daemon", - }, - - Username: "hakurei", - Shell: pathShell, - Home: pathDataData.Append(app.ID), ExtraPerms: []*hst.ExtraPermConfig{ {Path: dataHome, Execute: true}, {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, @@ -55,6 +39,23 @@ func withNixDaemon( {FilesystemConfig: &hst.FSLink{Target: container.AbsFHSUsrBin, Linkname: pathSwBin.String()}}, {FilesystemConfig: &hst.FSBind{Target: pathDataData.Append(app.ID), Source: pathSet.homeDir, Write: true, Ensure: true}}, }, + + Username: "hakurei", + Shell: pathShell, + Home: pathDataData.Append(app.ID), + + Path: pathShell, + Args: []string{bash, "-lc", "rm -f /nix/var/nix/daemon-socket/socket && " + + // start nix-daemon + "nix-daemon --store / & " + + // wait for socket to appear + "(while [ ! -S /nix/var/nix/daemon-socket/socket ]; do sleep 0.01; done) && " + + // create directory so nix stops complaining + "mkdir -p /nix/var/nix/profiles/per-user/root/channels && " + + strings.Join(command, " && ") + + // terminate nix-daemon + " && pkill nix-daemon", + }, }, }), dropShell, beforeFail) } @@ -67,12 +68,6 @@ func withCacheDir( mustRunAppDropShell(ctx, msg, &hst.Config{ ID: app.ID, - Path: pathShell, - Args: []string{bash, "-lc", strings.Join(command, " && ")}, - - Username: "nixos", - Shell: pathShell, - Home: pathDataData.Append(app.ID, "cache"), ExtraPerms: []*hst.ExtraPermConfig{ {Path: dataHome, Execute: true}, {Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, Execute: true}, @@ -94,13 +89,22 @@ func withCacheDir( {FilesystemConfig: &hst.FSBind{Source: workDir, Target: hst.AbsTmp.Append("bundle")}}, {FilesystemConfig: &hst.FSBind{Target: pathDataData.Append(app.ID, "cache"), Source: pathSet.cacheDir, Write: true, Ensure: true}}, }, + + Username: "nixos", + Shell: pathShell, + Home: pathDataData.Append(app.ID, "cache"), + + Path: pathShell, + Args: []string{bash, "-lc", strings.Join(command, " && ")}, }, }, dropShell, beforeFail) } func mustRunAppDropShell(ctx context.Context, msg container.Msg, config *hst.Config, dropShell bool, beforeFail func()) { if dropShell { - config.Args = []string{bash, "-l"} + if config.Container != nil { + config.Container.Args = []string{bash, "-l"} + } mustRunApp(ctx, msg, config, beforeFail) beforeFail() msg.BeforeExit() |
