hst/config: move container fields from toplevel
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m7s
Test / Hpkg (push) Successful in 3m54s
Test / Hakurei (race detector) (push) Successful in 5m18s
Test / Sandbox (race detector) (push) Successful in 2m10s
Test / Hakurei (push) Successful in 2m13s
Test / Flake checks (push) Successful in 1m33s
All checks were successful
Test / Create distribution (push) Successful in 33s
Test / Sandbox (push) Successful in 2m7s
Test / Hpkg (push) Successful in 3m54s
Test / Hakurei (race detector) (push) Successful in 5m18s
Test / Sandbox (race detector) (push) Successful in 2m10s
Test / Hakurei (push) Successful in 2m13s
Test / Flake checks (push) Successful in 1m33s
This change also moves pd behaviour to cmd/hakurei, as this does not belong in the hst API. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
f280994957
commit
9e48d7f562
@ -2,10 +2,12 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
@ -52,7 +54,9 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
|
|||||||
|
|
||||||
// config extraArgs...
|
// config extraArgs...
|
||||||
config := tryPath(msg, args[0])
|
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)
|
app.Main(ctx, msg, config)
|
||||||
panic("unreachable")
|
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 {
|
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 {
|
if flagIdentity < hst.IdentityMin || flagIdentity > hst.IdentityMax {
|
||||||
log.Fatalf("identity %d out of range", flagIdentity)
|
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" {
|
// paths are identical, resolve inner shell and program path
|
||||||
passwdOnce.Do(passwdFunc)
|
shell := container.AbsFHSRoot.Append("bin", "sh")
|
||||||
flagHomeDir = passwd.HomeDir
|
if a, err := container.NewAbs(os.Getenv("SHELL")); err == nil {
|
||||||
|
shell = 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if flagUserName == "chronos" {
|
var et hst.Enablement
|
||||||
passwdOnce.Do(passwdFunc)
|
if flagWayland {
|
||||||
flagUserName = passwd.Username
|
et |= hst.EWayland
|
||||||
|
}
|
||||||
|
if flagX11 {
|
||||||
|
et |= hst.EX11
|
||||||
|
}
|
||||||
|
if flagDBus {
|
||||||
|
et |= hst.EDBus
|
||||||
|
}
|
||||||
|
if flagPulse {
|
||||||
|
et |= hst.EPulse
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Identity = flagIdentity
|
config := &hst.Config{
|
||||||
config.Groups = flagGroups
|
ID: flagID,
|
||||||
config.Username = flagUserName
|
Identity: flagIdentity,
|
||||||
|
Groups: flagGroups,
|
||||||
|
Enablements: hst.NewEnablements(et),
|
||||||
|
|
||||||
if a, err := container.NewAbs(flagHomeDir); err != nil {
|
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())
|
log.Fatal(err.Error())
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
config.Home = a
|
config.Container.Home = a
|
||||||
}
|
}
|
||||||
|
|
||||||
var e hst.Enablement
|
|
||||||
if flagWayland {
|
|
||||||
e |= hst.EWayland
|
|
||||||
}
|
}
|
||||||
if flagX11 {
|
|
||||||
e |= hst.EX11
|
|
||||||
}
|
|
||||||
if flagDBus {
|
|
||||||
e |= hst.EDBus
|
|
||||||
}
|
|
||||||
if flagPulse {
|
|
||||||
e |= hst.EPulse
|
|
||||||
}
|
|
||||||
config.Enablements = hst.NewEnablements(e)
|
|
||||||
|
|
||||||
// parse D-Bus config file from flags if applicable
|
// parse D-Bus config file from flags if applicable
|
||||||
if flagDBus {
|
if flagDBus {
|
||||||
@ -218,7 +284,9 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
|
|||||||
if config == nil {
|
if config == nil {
|
||||||
config = tryPath(msg, name)
|
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:
|
default:
|
||||||
log.Fatal("show requires 1 argument")
|
log.Fatal("show requires 1 argument")
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"hakurei.app/container"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal/app"
|
"hakurei.app/internal/app"
|
||||||
"hakurei.app/internal/app/state"
|
"hakurei.app/internal/app/state"
|
||||||
@ -39,7 +40,9 @@ func printShowSystem(output io.Writer, short, flagJSON bool) {
|
|||||||
func printShowInstance(
|
func printShowInstance(
|
||||||
output io.Writer, now time.Time,
|
output io.Writer, now time.Time,
|
||||||
instance *state.State, config *hst.Config,
|
instance *state.State, config *hst.Config,
|
||||||
short, flagJSON bool) {
|
short, flagJSON bool) (valid bool) {
|
||||||
|
valid = true
|
||||||
|
|
||||||
if flagJSON {
|
if flagJSON {
|
||||||
if instance != nil {
|
if instance != nil {
|
||||||
printJSON(output, short, instance)
|
printJSON(output, short, instance)
|
||||||
@ -52,8 +55,11 @@ func printShowInstance(
|
|||||||
t := newPrinter(output)
|
t := newPrinter(output)
|
||||||
defer t.MustFlush()
|
defer t.MustFlush()
|
||||||
|
|
||||||
if config.Container == nil {
|
if err := config.Validate(); err != nil {
|
||||||
mustPrint(output, "Warning: this configuration uses permissive defaults!\n\n")
|
valid = false
|
||||||
|
if m, ok := container.GetErrorMessage(err); ok {
|
||||||
|
mustPrint(output, "Error: "+m+"!\n\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if instance != nil {
|
if instance != nil {
|
||||||
@ -73,11 +79,11 @@ func printShowInstance(
|
|||||||
if len(config.Groups) > 0 {
|
if len(config.Groups) > 0 {
|
||||||
t.Printf(" Groups:\t%s\n", strings.Join(config.Groups, ", "))
|
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 {
|
if config.Container != nil {
|
||||||
params := config.Container
|
params := config.Container
|
||||||
|
if params.Home != nil {
|
||||||
|
t.Printf(" Home:\t%s\n", params.Home)
|
||||||
|
}
|
||||||
if params.Hostname != "" {
|
if params.Hostname != "" {
|
||||||
t.Printf(" Hostname:\t%s\n", 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, " "))
|
t.Printf(" Flags:\t%s\n", strings.Join(flags, " "))
|
||||||
|
|
||||||
if config.Path != nil {
|
if params.Path != nil {
|
||||||
t.Printf(" Path:\t%s\n", config.Path)
|
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")
|
t.Printf("\n")
|
||||||
|
|
||||||
@ -114,6 +120,7 @@ func printShowInstance(
|
|||||||
t.Printf("Filesystem\n")
|
t.Printf("Filesystem\n")
|
||||||
for _, f := range config.Container.Filesystem {
|
for _, f := range config.Container.Filesystem {
|
||||||
if !f.Valid() {
|
if !f.Valid() {
|
||||||
|
valid = false
|
||||||
t.Println(" <invalid>")
|
t.Println(" <invalid>")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -161,6 +168,8 @@ func printShowInstance(
|
|||||||
printDBus(config.SystemBus)
|
printDBus(config.SystemBus)
|
||||||
t.Printf("\n")
|
t.Printf("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) {
|
func printPs(output io.Writer, now time.Time, s state.Store, short, flagJSON bool) {
|
||||||
|
@ -27,13 +27,14 @@ var (
|
|||||||
testAppTime = time.Unix(0, 9).UTC()
|
testAppTime = time.Unix(0, 9).UTC()
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_printShowInstance(t *testing.T) {
|
func TestPrintShowInstance(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
instance *state.State
|
instance *state.State
|
||||||
config *hst.Config
|
config *hst.Config
|
||||||
short, json bool
|
short, json bool
|
||||||
want string
|
want string
|
||||||
|
valid bool
|
||||||
}{
|
}{
|
||||||
{"config", nil, hst.Template(), false, false, `App
|
{"config", nil, hst.Template(), false, false, `App
|
||||||
Identity: 9 (org.chromium.Chromium)
|
Identity: 9 (org.chromium.Chromium)
|
||||||
@ -71,21 +72,25 @@ System bus
|
|||||||
Filter: true
|
Filter: true
|
||||||
Talk: ["org.bluez" "org.freedesktop.Avahi" "org.freedesktop.UPower"]
|
Talk: ["org.bluez" "org.freedesktop.Avahi" "org.freedesktop.UPower"]
|
||||||
|
|
||||||
`},
|
`, true},
|
||||||
{"config pd", nil, new(hst.Config), false, false, `Warning: this configuration uses permissive defaults!
|
{"config pd", nil, new(hst.Config), false, false, `Error: configuration missing container state!
|
||||||
|
|
||||||
App
|
App
|
||||||
Identity: 0
|
Identity: 0
|
||||||
Enablements: (no enablements)
|
Enablements: (no enablements)
|
||||||
|
|
||||||
`},
|
`, false},
|
||||||
{"config flag none", nil, &hst.Config{Container: new(hst.ContainerConfig)}, false, false, `App
|
{"config flag none", nil, &hst.Config{Container: new(hst.ContainerConfig)}, false, false, `Error: container configuration missing path to home directory!
|
||||||
|
|
||||||
|
App
|
||||||
Identity: 0
|
Identity: 0
|
||||||
Enablements: (no enablements)
|
Enablements: (no enablements)
|
||||||
Flags: none
|
Flags: none
|
||||||
|
|
||||||
`},
|
`, false},
|
||||||
{"config nil entries", nil, &hst.Config{Container: &hst.ContainerConfig{Filesystem: make([]hst.FilesystemConfigJSON, 1)}, ExtraPerms: make([]*hst.ExtraPermConfig, 1)}, false, false, `App
|
{"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
|
Identity: 0
|
||||||
Enablements: (no enablements)
|
Enablements: (no enablements)
|
||||||
Flags: none
|
Flags: none
|
||||||
@ -95,8 +100,8 @@ Filesystem
|
|||||||
|
|
||||||
Extra ACL
|
Extra ACL
|
||||||
|
|
||||||
`},
|
`, false},
|
||||||
{"config pd dbus see", nil, &hst.Config{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}, false, false, `Warning: this configuration uses permissive defaults!
|
{"config pd dbus see", nil, &hst.Config{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}, false, false, `Error: configuration missing container state!
|
||||||
|
|
||||||
App
|
App
|
||||||
Identity: 0
|
Identity: 0
|
||||||
@ -106,7 +111,7 @@ Session bus
|
|||||||
Filter: false
|
Filter: false
|
||||||
See: ["org.example.test"]
|
See: ["org.example.test"]
|
||||||
|
|
||||||
`},
|
`, false},
|
||||||
|
|
||||||
{"instance", testState, hst.Template(), false, false, `State
|
{"instance", testState, hst.Template(), false, false, `State
|
||||||
Instance: 8e2c76b066dabe574cf073bdb46eb5c1 (3735928559)
|
Instance: 8e2c76b066dabe574cf073bdb46eb5c1 (3735928559)
|
||||||
@ -148,8 +153,8 @@ System bus
|
|||||||
Filter: true
|
Filter: true
|
||||||
Talk: ["org.bluez" "org.freedesktop.Avahi" "org.freedesktop.UPower"]
|
Talk: ["org.bluez" "org.freedesktop.Avahi" "org.freedesktop.UPower"]
|
||||||
|
|
||||||
`},
|
`, true},
|
||||||
{"instance pd", testState, new(hst.Config), false, false, `Warning: this configuration uses permissive defaults!
|
{"instance pd", testState, new(hst.Config), false, false, `Error: configuration missing container state!
|
||||||
|
|
||||||
State
|
State
|
||||||
Instance: 8e2c76b066dabe574cf073bdb46eb5c1 (3735928559)
|
Instance: 8e2c76b066dabe574cf073bdb46eb5c1 (3735928559)
|
||||||
@ -159,10 +164,10 @@ App
|
|||||||
Identity: 0
|
Identity: 0
|
||||||
Enablements: (no enablements)
|
Enablements: (no enablements)
|
||||||
|
|
||||||
`},
|
`, false},
|
||||||
|
|
||||||
{"json nil", nil, nil, false, true, `null
|
{"json nil", nil, nil, false, true, `null
|
||||||
`},
|
`, true},
|
||||||
{"json instance", testState, nil, false, true, `{
|
{"json instance", testState, nil, false, true, `{
|
||||||
"instance": [
|
"instance": [
|
||||||
142,
|
142,
|
||||||
@ -185,14 +190,6 @@ App
|
|||||||
"pid": 3735928559,
|
"pid": 3735928559,
|
||||||
"config": {
|
"config": {
|
||||||
"id": "org.chromium.Chromium",
|
"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": {
|
"enablements": {
|
||||||
"wayland": true,
|
"wayland": true,
|
||||||
"dbus": true,
|
"dbus": true,
|
||||||
@ -234,9 +231,6 @@ App
|
|||||||
"broadcast": null,
|
"broadcast": null,
|
||||||
"filter": true
|
"filter": true
|
||||||
},
|
},
|
||||||
"username": "chronos",
|
|
||||||
"shell": "/run/current-system/sw/bin/zsh",
|
|
||||||
"home": "/data/data/org.chromium.Chromium",
|
|
||||||
"extra_perms": [
|
"extra_perms": [
|
||||||
{
|
{
|
||||||
"ensure": true,
|
"ensure": true,
|
||||||
@ -331,22 +325,25 @@ App
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": 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"
|
"time": "1970-01-01T00:00:00.000000009Z"
|
||||||
}
|
}
|
||||||
`},
|
`, true},
|
||||||
{"json config", nil, hst.Template(), false, true, `{
|
{"json config", nil, hst.Template(), false, true, `{
|
||||||
"id": "org.chromium.Chromium",
|
"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": {
|
"enablements": {
|
||||||
"wayland": true,
|
"wayland": true,
|
||||||
"dbus": true,
|
"dbus": true,
|
||||||
@ -388,9 +385,6 @@ App
|
|||||||
"broadcast": null,
|
"broadcast": null,
|
||||||
"filter": true
|
"filter": true
|
||||||
},
|
},
|
||||||
"username": "chronos",
|
|
||||||
"shell": "/run/current-system/sw/bin/zsh",
|
|
||||||
"home": "/data/data/org.chromium.Chromium",
|
|
||||||
"extra_perms": [
|
"extra_perms": [
|
||||||
{
|
{
|
||||||
"ensure": true,
|
"ensure": true,
|
||||||
@ -485,26 +479,39 @@ App
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": 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 {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
output := new(strings.Builder)
|
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 {
|
if got := output.String(); got != tc.want {
|
||||||
t.Errorf("printShowInstance: got\n%s\nwant\n%s",
|
t.Errorf("printShowInstance: \n%s\nwant\n%s", got, tc.want)
|
||||||
got, tc.want)
|
|
||||||
return
|
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 {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
entries state.Entries
|
entries state.Entries
|
||||||
@ -547,14 +554,6 @@ func Test_printPs(t *testing.T) {
|
|||||||
"pid": 3735928559,
|
"pid": 3735928559,
|
||||||
"config": {
|
"config": {
|
||||||
"id": "org.chromium.Chromium",
|
"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": {
|
"enablements": {
|
||||||
"wayland": true,
|
"wayland": true,
|
||||||
"dbus": true,
|
"dbus": true,
|
||||||
@ -596,9 +595,6 @@ func Test_printPs(t *testing.T) {
|
|||||||
"broadcast": null,
|
"broadcast": null,
|
||||||
"filter": true
|
"filter": true
|
||||||
},
|
},
|
||||||
"username": "chronos",
|
|
||||||
"shell": "/run/current-system/sw/bin/zsh",
|
|
||||||
"home": "/data/data/org.chromium.Chromium",
|
|
||||||
"extra_perms": [
|
"extra_perms": [
|
||||||
{
|
{
|
||||||
"ensure": true,
|
"ensure": true,
|
||||||
@ -693,6 +689,17 @@ func Test_printPs(t *testing.T) {
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": 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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -66,19 +66,12 @@ func (app *appInfo) toHst(pathSet *appPathSet, pathname *container.Absolute, arg
|
|||||||
config := &hst.Config{
|
config := &hst.Config{
|
||||||
ID: app.ID,
|
ID: app.ID,
|
||||||
|
|
||||||
Path: pathname,
|
|
||||||
Args: argv,
|
|
||||||
|
|
||||||
Enablements: app.Enablements,
|
Enablements: app.Enablements,
|
||||||
|
|
||||||
SystemBus: app.SystemBus,
|
SystemBus: app.SystemBus,
|
||||||
SessionBus: app.SessionBus,
|
SessionBus: app.SessionBus,
|
||||||
DirectWayland: app.DirectWayland,
|
DirectWayland: app.DirectWayland,
|
||||||
|
|
||||||
Username: "hakurei",
|
|
||||||
Shell: pathShell,
|
|
||||||
Home: pathDataData.Append(app.ID),
|
|
||||||
|
|
||||||
Identity: app.Identity,
|
Identity: app.Identity,
|
||||||
Groups: app.Groups,
|
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{Source: container.AbsFHSSys.Append("devices"), Optional: true}},
|
||||||
{FilesystemConfig: &hst.FSBind{Target: pathDataData.Append(app.ID), Source: pathSet.homeDir, Write: true, Ensure: 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{
|
ExtraPerms: []*hst.ExtraPermConfig{
|
||||||
{Path: dataHome, Execute: true},
|
{Path: dataHome, Execute: true},
|
||||||
|
@ -62,11 +62,11 @@ def check_state(name, enablements):
|
|||||||
|
|
||||||
config = instance['config']
|
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]):
|
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 {instance['config']['args']}")
|
raise Exception(f"unexpected args {config['container']['args']}")
|
||||||
|
|
||||||
if config['enablements'] != enablements:
|
if config['enablements'] != enablements:
|
||||||
raise Exception(f"unexpected enablements {instance['config']['enablements']}")
|
raise Exception(f"unexpected enablements {config['enablements']}")
|
||||||
|
|
||||||
|
|
||||||
start_all()
|
start_all()
|
||||||
|
@ -18,22 +18,6 @@ func withNixDaemon(
|
|||||||
mustRunAppDropShell(ctx, msg, updateConfig(&hst.Config{
|
mustRunAppDropShell(ctx, msg, updateConfig(&hst.Config{
|
||||||
ID: app.ID,
|
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{
|
ExtraPerms: []*hst.ExtraPermConfig{
|
||||||
{Path: dataHome, Execute: true},
|
{Path: dataHome, Execute: true},
|
||||||
{Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, 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.FSLink{Target: container.AbsFHSUsrBin, Linkname: pathSwBin.String()}},
|
||||||
{FilesystemConfig: &hst.FSBind{Target: pathDataData.Append(app.ID), Source: pathSet.homeDir, Write: true, Ensure: 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: 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)
|
}), dropShell, beforeFail)
|
||||||
}
|
}
|
||||||
@ -67,12 +68,6 @@ func withCacheDir(
|
|||||||
mustRunAppDropShell(ctx, msg, &hst.Config{
|
mustRunAppDropShell(ctx, msg, &hst.Config{
|
||||||
ID: app.ID,
|
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{
|
ExtraPerms: []*hst.ExtraPermConfig{
|
||||||
{Path: dataHome, Execute: true},
|
{Path: dataHome, Execute: true},
|
||||||
{Ensure: true, Path: pathSet.baseDir, Read: true, Write: true, 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{Source: workDir, Target: hst.AbsTmp.Append("bundle")}},
|
||||||
{FilesystemConfig: &hst.FSBind{Target: pathDataData.Append(app.ID, "cache"), Source: pathSet.cacheDir, Write: true, Ensure: true}},
|
{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)
|
}, dropShell, beforeFail)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustRunAppDropShell(ctx context.Context, msg container.Msg, config *hst.Config, dropShell bool, beforeFail func()) {
|
func mustRunAppDropShell(ctx context.Context, msg container.Msg, config *hst.Config, dropShell bool, beforeFail func()) {
|
||||||
if dropShell {
|
if dropShell {
|
||||||
config.Args = []string{bash, "-l"}
|
if config.Container != nil {
|
||||||
|
config.Container.Args = []string{bash, "-l"}
|
||||||
|
}
|
||||||
mustRunApp(ctx, msg, config, beforeFail)
|
mustRunApp(ctx, msg, config, beforeFail)
|
||||||
beforeFail()
|
beforeFail()
|
||||||
msg.BeforeExit()
|
msg.BeforeExit()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hst
|
package hst
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
@ -35,11 +36,6 @@ type (
|
|||||||
// Passed to wayland security-context-v1 and used as part of defaults in dbus session proxy.
|
// Passed to wayland security-context-v1 and used as part of defaults in dbus session proxy.
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|
||||||
// Pathname to executable file in the container filesystem.
|
|
||||||
Path *container.Absolute `json:"path,omitempty"`
|
|
||||||
// Final args passed to the initial program.
|
|
||||||
Args []string `json:"args"`
|
|
||||||
|
|
||||||
// System services to make available in the container.
|
// System services to make available in the container.
|
||||||
Enablements *Enablements `json:"enablements,omitempty"`
|
Enablements *Enablements `json:"enablements,omitempty"`
|
||||||
|
|
||||||
@ -53,14 +49,6 @@ type (
|
|||||||
// and the bare socket is made available to the container.
|
// and the bare socket is made available to the container.
|
||||||
DirectWayland bool `json:"direct_wayland,omitempty"`
|
DirectWayland bool `json:"direct_wayland,omitempty"`
|
||||||
|
|
||||||
// String used as the username of the emulated user, validated against the default NAME_REGEX from adduser.
|
|
||||||
// Defaults to passwd name of target uid or chronos.
|
|
||||||
Username string `json:"username,omitempty"`
|
|
||||||
// Pathname of shell in the container filesystem to use for the emulated user.
|
|
||||||
Shell *container.Absolute `json:"shell"`
|
|
||||||
// Directory in the container filesystem to enter and use as the home directory of the emulated user.
|
|
||||||
Home *container.Absolute `json:"home"`
|
|
||||||
|
|
||||||
// Extra acl update ops to perform before setuid.
|
// Extra acl update ops to perform before setuid.
|
||||||
ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"`
|
ExtraPerms []*ExtraPermConfig `json:"extra_perms,omitempty"`
|
||||||
|
|
||||||
@ -114,9 +102,50 @@ type (
|
|||||||
|
|
||||||
If the first element targets /, it is inserted early and excluded from path hiding. */
|
If the first element targets /, it is inserted early and excluded from path hiding. */
|
||||||
Filesystem []FilesystemConfigJSON `json:"filesystem"`
|
Filesystem []FilesystemConfigJSON `json:"filesystem"`
|
||||||
|
|
||||||
|
// String used as the username of the emulated user, validated against the default NAME_REGEX from adduser.
|
||||||
|
// Defaults to passwd name of target uid or chronos.
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
// Pathname of shell in the container filesystem to use for the emulated user.
|
||||||
|
Shell *container.Absolute `json:"shell"`
|
||||||
|
// Directory in the container filesystem to enter and use as the home directory of the emulated user.
|
||||||
|
Home *container.Absolute `json:"home"`
|
||||||
|
|
||||||
|
// Pathname to executable file in the container filesystem.
|
||||||
|
Path *container.Absolute `json:"path,omitempty"`
|
||||||
|
// Final args passed to the initial program.
|
||||||
|
Args []string `json:"args"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrConfigNull is returned by [Config.Validate] for an invalid configuration that contains a null value for any
|
||||||
|
// field that must not be null.
|
||||||
|
var ErrConfigNull = errors.New("unexpected null in config")
|
||||||
|
|
||||||
|
func (config *Config) Validate() error {
|
||||||
|
if config == nil {
|
||||||
|
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
|
||||||
|
Msg: "invalid configuration"}
|
||||||
|
}
|
||||||
|
if config.Container == nil {
|
||||||
|
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
|
||||||
|
Msg: "configuration missing container state"}
|
||||||
|
}
|
||||||
|
if config.Container.Home == nil {
|
||||||
|
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
|
||||||
|
Msg: "container configuration missing path to home directory"}
|
||||||
|
}
|
||||||
|
if config.Container.Shell == nil {
|
||||||
|
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
|
||||||
|
Msg: "container configuration missing path to shell"}
|
||||||
|
}
|
||||||
|
if config.Container.Path == nil {
|
||||||
|
return &AppError{Step: "validate configuration", Err: ErrConfigNull,
|
||||||
|
Msg: "container configuration missing path to initial program"}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ExtraPermConfig describes an acl update op.
|
// ExtraPermConfig describes an acl update op.
|
||||||
type ExtraPermConfig struct {
|
type ExtraPermConfig struct {
|
||||||
Ensure bool `json:"ensure,omitempty"`
|
Ensure bool `json:"ensure,omitempty"`
|
||||||
|
@ -1,12 +1,49 @@
|
|||||||
package hst_test
|
package hst_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestConfigValidate(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
config *hst.Config
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
{"nil", nil, &hst.AppError{Step: "validate configuration", Err: hst.ErrConfigNull,
|
||||||
|
Msg: "invalid configuration"}},
|
||||||
|
{"container", &hst.Config{}, &hst.AppError{Step: "validate configuration", Err: hst.ErrConfigNull,
|
||||||
|
Msg: "configuration missing container state"}},
|
||||||
|
{"home", &hst.Config{Container: &hst.ContainerConfig{}}, &hst.AppError{Step: "validate configuration", Err: hst.ErrConfigNull,
|
||||||
|
Msg: "container configuration missing path to home directory"}},
|
||||||
|
{"shell", &hst.Config{Container: &hst.ContainerConfig{
|
||||||
|
Home: container.AbsFHSTmp,
|
||||||
|
}}, &hst.AppError{Step: "validate configuration", Err: hst.ErrConfigNull,
|
||||||
|
Msg: "container configuration missing path to shell"}},
|
||||||
|
{"path", &hst.Config{Container: &hst.ContainerConfig{
|
||||||
|
Home: container.AbsFHSTmp,
|
||||||
|
Shell: container.AbsFHSTmp,
|
||||||
|
}}, &hst.AppError{Step: "validate configuration", Err: hst.ErrConfigNull,
|
||||||
|
Msg: "container configuration missing path to initial program"}},
|
||||||
|
{"valid", &hst.Config{Container: &hst.ContainerConfig{
|
||||||
|
Home: container.AbsFHSTmp,
|
||||||
|
Shell: container.AbsFHSTmp,
|
||||||
|
Path: container.AbsFHSTmp,
|
||||||
|
}}, nil},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if err := tc.config.Validate(); !reflect.DeepEqual(err, tc.wantErr) {
|
||||||
|
t.Errorf("Validate: error = %#v, want %#v", err, tc.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestExtraPermConfig(t *testing.T) {
|
func TestExtraPermConfig(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
25
hst/hst.go
25
hst/hst.go
@ -60,15 +60,6 @@ func Template() *Config {
|
|||||||
return &Config{
|
return &Config{
|
||||||
ID: "org.chromium.Chromium",
|
ID: "org.chromium.Chromium",
|
||||||
|
|
||||||
Path: container.AbsFHSRun.Append("current-system/sw/bin/chromium"),
|
|
||||||
Args: []string{
|
|
||||||
"chromium",
|
|
||||||
"--ignore-gpu-blocklist",
|
|
||||||
"--disable-smooth-scrolling",
|
|
||||||
"--enable-features=UseOzonePlatform",
|
|
||||||
"--ozone-platform=wayland",
|
|
||||||
},
|
|
||||||
|
|
||||||
Enablements: NewEnablements(EWayland | EDBus | EPulse),
|
Enablements: NewEnablements(EWayland | EDBus | EPulse),
|
||||||
|
|
||||||
SessionBus: &dbus.Config{
|
SessionBus: &dbus.Config{
|
||||||
@ -93,9 +84,6 @@ func Template() *Config {
|
|||||||
},
|
},
|
||||||
DirectWayland: false,
|
DirectWayland: false,
|
||||||
|
|
||||||
Username: "chronos",
|
|
||||||
Shell: container.AbsFHSRun.Append("current-system/sw/bin/zsh"),
|
|
||||||
Home: container.MustAbs("/data/data/org.chromium.Chromium"),
|
|
||||||
ExtraPerms: []*ExtraPermConfig{
|
ExtraPerms: []*ExtraPermConfig{
|
||||||
{Path: container.AbsFHSVarLib.Append("hakurei/u0"), Ensure: true, Execute: true},
|
{Path: container.AbsFHSVarLib.Append("hakurei/u0"), Ensure: true, Execute: true},
|
||||||
{Path: container.AbsFHSVarLib.Append("hakurei/u0/org.chromium.Chromium"), Read: true, Write: true, Execute: true},
|
{Path: container.AbsFHSVarLib.Append("hakurei/u0/org.chromium.Chromium"), Read: true, Write: true, Execute: true},
|
||||||
@ -140,6 +128,19 @@ func Template() *Config {
|
|||||||
Target: container.MustAbs("/data/data/org.chromium.Chromium"), Write: true, Ensure: true}},
|
Target: container.MustAbs("/data/data/org.chromium.Chromium"), Write: true, Ensure: true}},
|
||||||
{&FSBind{Source: container.AbsFHSDev.Append("dri"), Device: true, Optional: true}},
|
{&FSBind{Source: container.AbsFHSDev.Append("dri"), Device: true, Optional: true}},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Username: "chronos",
|
||||||
|
Shell: container.AbsFHSRun.Append("current-system/sw/bin/zsh"),
|
||||||
|
Home: container.MustAbs("/data/data/org.chromium.Chromium"),
|
||||||
|
|
||||||
|
Path: container.AbsFHSRun.Append("current-system/sw/bin/chromium"),
|
||||||
|
Args: []string{
|
||||||
|
"chromium",
|
||||||
|
"--ignore-gpu-blocklist",
|
||||||
|
"--disable-smooth-scrolling",
|
||||||
|
"--enable-features=UseOzonePlatform",
|
||||||
|
"--ozone-platform=wayland",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,14 +92,6 @@ func TestAppError(t *testing.T) {
|
|||||||
func TestTemplate(t *testing.T) {
|
func TestTemplate(t *testing.T) {
|
||||||
const want = `{
|
const want = `{
|
||||||
"id": "org.chromium.Chromium",
|
"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": {
|
"enablements": {
|
||||||
"wayland": true,
|
"wayland": true,
|
||||||
"dbus": true,
|
"dbus": true,
|
||||||
@ -141,9 +133,6 @@ func TestTemplate(t *testing.T) {
|
|||||||
"broadcast": null,
|
"broadcast": null,
|
||||||
"filter": true
|
"filter": true
|
||||||
},
|
},
|
||||||
"username": "chronos",
|
|
||||||
"shell": "/run/current-system/sw/bin/zsh",
|
|
||||||
"home": "/data/data/org.chromium.Chromium",
|
|
||||||
"extra_perms": [
|
"extra_perms": [
|
||||||
{
|
{
|
||||||
"ensure": true,
|
"ensure": true,
|
||||||
@ -238,6 +227,17 @@ func TestTemplate(t *testing.T) {
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": 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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
@ -37,7 +37,35 @@ func TestApp(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"nixos permissive defaults no enablements", new(stubNixOS),
|
"nixos permissive defaults no enablements", new(stubNixOS),
|
||||||
&hst.Config{Username: "chronos", Home: m("/home/chronos")},
|
&hst.Config{Container: &hst.ContainerConfig{
|
||||||
|
Userns: true, HostNet: true, HostAbstract: true, Tty: true,
|
||||||
|
|
||||||
|
Filesystem: []hst.FilesystemConfigJSON{
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Target: container.AbsFHSRoot,
|
||||||
|
Source: container.AbsFHSRoot,
|
||||||
|
Write: true,
|
||||||
|
Special: true,
|
||||||
|
}},
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Source: container.AbsFHSDev.Append("kvm"),
|
||||||
|
Device: true,
|
||||||
|
Optional: true,
|
||||||
|
}},
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Target: container.AbsFHSEtc,
|
||||||
|
Source: container.AbsFHSEtc,
|
||||||
|
Special: true,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
|
||||||
|
Username: "chronos",
|
||||||
|
Shell: m("/run/current-system/sw/bin/zsh"),
|
||||||
|
Home: m("/home/chronos"),
|
||||||
|
|
||||||
|
Path: m("/run/current-system/sw/bin/zsh"),
|
||||||
|
Args: []string{"/run/current-system/sw/bin/zsh"},
|
||||||
|
}},
|
||||||
state.ID{
|
state.ID{
|
||||||
0x4a, 0x45, 0x0b, 0x65,
|
0x4a, 0x45, 0x0b, 0x65,
|
||||||
0x96, 0xd7, 0xbc, 0x15,
|
0x96, 0xd7, 0xbc, 0x15,
|
||||||
@ -70,7 +98,6 @@ func TestApp(t *testing.T) {
|
|||||||
DevWritable(m("/dev/"), true).
|
DevWritable(m("/dev/"), true).
|
||||||
Tmpfs(m("/dev/shm"), 0, 01777).
|
Tmpfs(m("/dev/shm"), 0, 01777).
|
||||||
Bind(m("/dev/kvm"), m("/dev/kvm"), container.BindWritable|container.BindDevice|container.BindOptional).
|
Bind(m("/dev/kvm"), m("/dev/kvm"), container.BindWritable|container.BindDevice|container.BindOptional).
|
||||||
Readonly(m("/var/run/nscd"), 0755).
|
|
||||||
Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac").
|
Etc(m("/etc/"), "4a450b6596d7bc15bd01780eb9a607ac").
|
||||||
Tmpfs(m("/run/user/1971"), 8192, 0755).
|
Tmpfs(m("/run/user/1971"), 8192, 0755).
|
||||||
Tmpfs(m("/run/nscd"), 8192, 0755).
|
Tmpfs(m("/run/nscd"), 8192, 0755).
|
||||||
@ -93,11 +120,8 @@ func TestApp(t *testing.T) {
|
|||||||
"nixos permissive defaults chromium", new(stubNixOS),
|
"nixos permissive defaults chromium", new(stubNixOS),
|
||||||
&hst.Config{
|
&hst.Config{
|
||||||
ID: "org.chromium.Chromium",
|
ID: "org.chromium.Chromium",
|
||||||
Args: []string{"zsh", "-c", "exec chromium "},
|
|
||||||
Identity: 9,
|
Identity: 9,
|
||||||
Groups: []string{"video"},
|
Groups: []string{"video"},
|
||||||
Username: "chronos",
|
|
||||||
Home: m("/home/chronos"),
|
|
||||||
SessionBus: &dbus.Config{
|
SessionBus: &dbus.Config{
|
||||||
Talk: []string{
|
Talk: []string{
|
||||||
"org.freedesktop.Notifications",
|
"org.freedesktop.Notifications",
|
||||||
@ -130,6 +154,41 @@ func TestApp(t *testing.T) {
|
|||||||
Filter: true,
|
Filter: true,
|
||||||
},
|
},
|
||||||
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
|
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
|
||||||
|
|
||||||
|
Container: &hst.ContainerConfig{
|
||||||
|
Userns: true, HostNet: true, HostAbstract: true, Tty: true,
|
||||||
|
|
||||||
|
Filesystem: []hst.FilesystemConfigJSON{
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Target: container.AbsFHSRoot,
|
||||||
|
Source: container.AbsFHSRoot,
|
||||||
|
Write: true,
|
||||||
|
Special: true,
|
||||||
|
}},
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Source: container.AbsFHSDev.Append("dri"),
|
||||||
|
Device: true,
|
||||||
|
Optional: true,
|
||||||
|
}},
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Source: container.AbsFHSDev.Append("kvm"),
|
||||||
|
Device: true,
|
||||||
|
Optional: true,
|
||||||
|
}},
|
||||||
|
{FilesystemConfig: &hst.FSBind{
|
||||||
|
Target: container.AbsFHSEtc,
|
||||||
|
Source: container.AbsFHSEtc,
|
||||||
|
Special: true,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
|
||||||
|
Username: "chronos",
|
||||||
|
Shell: m("/run/current-system/sw/bin/zsh"),
|
||||||
|
Home: m("/home/chronos"),
|
||||||
|
|
||||||
|
Path: m("/run/current-system/sw/bin/zsh"),
|
||||||
|
Args: []string{"zsh", "-c", "exec chromium "},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
state.ID{
|
state.ID{
|
||||||
0xeb, 0xf0, 0x83, 0xd1,
|
0xeb, 0xf0, 0x83, 0xd1,
|
||||||
@ -207,7 +266,6 @@ func TestApp(t *testing.T) {
|
|||||||
Tmpfs(m("/dev/shm"), 0, 01777).
|
Tmpfs(m("/dev/shm"), 0, 01777).
|
||||||
Bind(m("/dev/dri"), m("/dev/dri"), container.BindWritable|container.BindDevice|container.BindOptional).
|
Bind(m("/dev/dri"), m("/dev/dri"), container.BindWritable|container.BindDevice|container.BindOptional).
|
||||||
Bind(m("/dev/kvm"), m("/dev/kvm"), container.BindWritable|container.BindDevice|container.BindOptional).
|
Bind(m("/dev/kvm"), m("/dev/kvm"), container.BindWritable|container.BindDevice|container.BindOptional).
|
||||||
Readonly(m("/var/run/nscd"), 0755).
|
|
||||||
Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c").
|
Etc(m("/etc/"), "ebf083d1b175911782d413369b64ce7c").
|
||||||
Tmpfs(m("/run/user/1971"), 8192, 0755).
|
Tmpfs(m("/run/user/1971"), 8192, 0755).
|
||||||
Tmpfs(m("/run/nscd"), 8192, 0755).
|
Tmpfs(m("/run/nscd"), 8192, 0755).
|
||||||
@ -236,10 +294,7 @@ func TestApp(t *testing.T) {
|
|||||||
"nixos chromium direct wayland", new(stubNixOS),
|
"nixos chromium direct wayland", new(stubNixOS),
|
||||||
&hst.Config{
|
&hst.Config{
|
||||||
ID: "org.chromium.Chromium",
|
ID: "org.chromium.Chromium",
|
||||||
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
|
|
||||||
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
|
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
|
||||||
Shell: m("/run/current-system/sw/bin/zsh"),
|
|
||||||
|
|
||||||
Container: &hst.ContainerConfig{
|
Container: &hst.ContainerConfig{
|
||||||
Userns: true, HostNet: true, MapRealUID: true, Env: nil,
|
Userns: true, HostNet: true, MapRealUID: true, Env: nil,
|
||||||
Filesystem: []hst.FilesystemConfigJSON{
|
Filesystem: []hst.FilesystemConfigJSON{
|
||||||
@ -257,6 +312,12 @@ func TestApp(t *testing.T) {
|
|||||||
f(&hst.FSBind{Source: m("/etc/"), Target: m("/etc/"), Special: true}),
|
f(&hst.FSBind{Source: m("/etc/"), Target: m("/etc/"), Special: true}),
|
||||||
f(&hst.FSBind{Source: m("/var/lib/persist/module/hakurei/0/1"), Write: true, Ensure: true}),
|
f(&hst.FSBind{Source: m("/var/lib/persist/module/hakurei/0/1"), Write: true, Ensure: true}),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Username: "u0_a1",
|
||||||
|
Shell: m("/run/current-system/sw/bin/zsh"),
|
||||||
|
Home: m("/var/lib/persist/module/hakurei/0/1"),
|
||||||
|
|
||||||
|
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
|
||||||
},
|
},
|
||||||
SystemBus: &dbus.Config{
|
SystemBus: &dbus.Config{
|
||||||
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
|
Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"},
|
||||||
@ -278,8 +339,6 @@ func TestApp(t *testing.T) {
|
|||||||
},
|
},
|
||||||
DirectWayland: true,
|
DirectWayland: true,
|
||||||
|
|
||||||
Username: "u0_a1",
|
|
||||||
Home: m("/var/lib/persist/module/hakurei/0/1"),
|
|
||||||
Identity: 1, Groups: []string{},
|
Identity: 1, Groups: []string{},
|
||||||
},
|
},
|
||||||
state.ID{
|
state.ID{
|
||||||
@ -461,7 +520,6 @@ func (s stubOsFileReadCloser) Write([]byte) (int, error) { panic("attempting to
|
|||||||
func (s stubOsFileReadCloser) Stat() (fs.FileInfo, error) { panic("attempting to call Stat") }
|
func (s stubOsFileReadCloser) Stat() (fs.FileInfo, error) { panic("attempting to call Stat") }
|
||||||
|
|
||||||
type stubNixOS struct {
|
type stubNixOS struct {
|
||||||
lookPathErr map[string]error
|
|
||||||
usernameErr map[string]error
|
usernameErr map[string]error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,21 +675,6 @@ func (k *stubNixOS) evalSymlinks(path string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *stubNixOS) lookPath(file string) (string, error) {
|
|
||||||
if k.lookPathErr != nil {
|
|
||||||
if err, ok := k.lookPathErr[file]; ok {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch file {
|
|
||||||
case "zsh":
|
|
||||||
return "/run/current-system/sw/bin/zsh", nil
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("attempted to look up unexpected executable %q", file))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *stubNixOS) lookupGroupId(name string) (string, error) {
|
func (k *stubNixOS) lookupGroupId(name string) (string, error) {
|
||||||
switch name {
|
switch name {
|
||||||
case "video":
|
case "video":
|
||||||
|
@ -45,9 +45,6 @@ type syscallDispatcher interface {
|
|||||||
// evalSymlinks provides [filepath.EvalSymlinks].
|
// evalSymlinks provides [filepath.EvalSymlinks].
|
||||||
evalSymlinks(path string) (string, error)
|
evalSymlinks(path string) (string, error)
|
||||||
|
|
||||||
// lookPath provides exec.LookPath.
|
|
||||||
lookPath(file string) (string, error)
|
|
||||||
|
|
||||||
// lookupGroupId calls [user.LookupGroup] and returns the Gid field of the resulting [user.Group] struct.
|
// lookupGroupId calls [user.LookupGroup] and returns the Gid field of the resulting [user.Group] struct.
|
||||||
lookupGroupId(name string) (string, error)
|
lookupGroupId(name string) (string, error)
|
||||||
|
|
||||||
@ -81,8 +78,6 @@ func (direct) tempdir() string { return os.TempDir()
|
|||||||
|
|
||||||
func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
|
func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
|
||||||
|
|
||||||
func (direct) lookPath(file string) (string, error) { return exec.LookPath(file) }
|
|
||||||
|
|
||||||
func (direct) lookupGroupId(name string) (gid string, err error) {
|
func (direct) lookupGroupId(name string) (gid string, err error) {
|
||||||
var group *user.Group
|
var group *user.Group
|
||||||
group, err = user.LookupGroup(name)
|
group, err = user.LookupGroup(name)
|
||||||
|
@ -18,7 +18,6 @@ func (panicDispatcher) open(string) (osFile, error) { panic("unreachab
|
|||||||
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
|
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) tempdir() string { panic("unreachable") }
|
func (panicDispatcher) tempdir() string { panic("unreachable") }
|
||||||
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
|
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) lookPath(string) (string, error) { panic("unreachable") }
|
|
||||||
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
|
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
|
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) overflowUid(container.Msg) int { panic("unreachable") }
|
func (panicDispatcher) overflowUid(container.Msg) int { panic("unreachable") }
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
@ -66,11 +65,8 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, id *state.ID,
|
|||||||
}
|
}
|
||||||
k.ctx = ctx
|
k.ctx = ctx
|
||||||
|
|
||||||
if config == nil {
|
if err := config.Validate(); err != nil {
|
||||||
return newWithMessage("invalid configuration")
|
return err
|
||||||
}
|
|
||||||
if config.Home == nil {
|
|
||||||
return newWithMessage("invalid path to home directory")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(ophestra): do not clobber during finalise
|
// TODO(ophestra): do not clobber during finalise
|
||||||
@ -102,6 +98,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, id *state.ID,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validation complete at this point
|
||||||
s := outcomeState{
|
s := outcomeState{
|
||||||
ID: id,
|
ID: id,
|
||||||
Identity: config.Identity,
|
Identity: config.Identity,
|
||||||
@ -110,81 +107,6 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, id *state.ID,
|
|||||||
Container: config.Container,
|
Container: config.Container,
|
||||||
}
|
}
|
||||||
|
|
||||||
// permissive defaults
|
|
||||||
if s.Container == nil {
|
|
||||||
msg.Verbose("container configuration not supplied, PROCEED WITH CAUTION")
|
|
||||||
|
|
||||||
if config.Shell == nil {
|
|
||||||
config.Shell = container.AbsFHSRoot.Append("bin", "sh")
|
|
||||||
shell, _ := k.lookupEnv("SHELL")
|
|
||||||
if a, err := container.NewAbs(shell); err == nil {
|
|
||||||
config.Shell = a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hsu clears the environment so resolve paths early
|
|
||||||
if config.Path == nil {
|
|
||||||
if len(config.Args) > 0 {
|
|
||||||
if p, err := k.lookPath(config.Args[0]); err != nil {
|
|
||||||
return &hst.AppError{Step: "look up executable file", Err: err}
|
|
||||||
} else if config.Path, err = container.NewAbs(p); err != nil {
|
|
||||||
return newWithMessageError(err.Error(), err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
config.Path = config.Shell
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := &hst.ContainerConfig{
|
|
||||||
Userns: true,
|
|
||||||
HostNet: true,
|
|
||||||
HostAbstract: true,
|
|
||||||
Tty: true,
|
|
||||||
|
|
||||||
Filesystem: []hst.FilesystemConfigJSON{
|
|
||||||
// autoroot, includes the home directory
|
|
||||||
{FilesystemConfig: &hst.FSBind{
|
|
||||||
Target: container.AbsFHSRoot,
|
|
||||||
Source: container.AbsFHSRoot,
|
|
||||||
Write: true,
|
|
||||||
Special: true,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind GPU stuff
|
|
||||||
if config.Enablements.Unwrap()&(hst.EX11|hst.EWayland) != 0 {
|
|
||||||
conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{Source: container.AbsFHSDev.Append("dri"), Device: true, Optional: true}})
|
|
||||||
}
|
|
||||||
// opportunistically bind kvm
|
|
||||||
conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{Source: container.AbsFHSDev.Append("kvm"), Device: true, Optional: true}})
|
|
||||||
|
|
||||||
// hide nscd from container if present
|
|
||||||
nscd := container.AbsFHSVar.Append("run/nscd")
|
|
||||||
if _, err := k.stat(nscd.String()); !errors.Is(err, fs.ErrNotExist) {
|
|
||||||
conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSEphemeral{Target: nscd}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// do autoetc last
|
|
||||||
conf.Filesystem = append(conf.Filesystem,
|
|
||||||
hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{
|
|
||||||
Target: container.AbsFHSEtc,
|
|
||||||
Source: container.AbsFHSEtc,
|
|
||||||
Special: true,
|
|
||||||
}},
|
|
||||||
)
|
|
||||||
|
|
||||||
s.Container = conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// late nil checks for pd behaviour
|
|
||||||
if config.Shell == nil {
|
|
||||||
return newWithMessage("invalid shell path")
|
|
||||||
}
|
|
||||||
if config.Path == nil {
|
|
||||||
return newWithMessage("invalid program path")
|
|
||||||
}
|
|
||||||
|
|
||||||
// enforce bounds and default early
|
// enforce bounds and default early
|
||||||
if s.Container.WaitDelay <= 0 {
|
if s.Container.WaitDelay <= 0 {
|
||||||
kp.waitDelay = hst.WaitDelayDefault
|
kp.waitDelay = hst.WaitDelayDefault
|
||||||
@ -210,14 +132,14 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, id *state.ID,
|
|||||||
{
|
{
|
||||||
ops := []outcomeOp{
|
ops := []outcomeOp{
|
||||||
// must run first
|
// must run first
|
||||||
&spParamsOp{Path: config.Path, Args: config.Args},
|
&spParamsOp{},
|
||||||
|
|
||||||
// TODO(ophestra): move this late for #8 and #9
|
// TODO(ophestra): move this late for #8 and #9
|
||||||
spFilesystemOp{},
|
spFilesystemOp{},
|
||||||
|
|
||||||
spRuntimeOp{},
|
spRuntimeOp{},
|
||||||
spTmpdirOp{},
|
spTmpdirOp{},
|
||||||
&spAccountOp{Home: config.Home, Username: config.Username, Shell: config.Shell},
|
spAccountOp{},
|
||||||
}
|
}
|
||||||
|
|
||||||
et := config.Enablements.Unwrap()
|
et := config.Enablements.Unwrap()
|
||||||
|
@ -9,45 +9,38 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// spAccountOp sets up user account emulation inside the container.
|
// spAccountOp sets up user account emulation inside the container.
|
||||||
type spAccountOp struct {
|
type spAccountOp struct{}
|
||||||
// Inner directory to use as the home directory of the emulated user.
|
|
||||||
Home *container.Absolute
|
|
||||||
// String matching the default NAME_REGEX value from adduser to use as the username of the emulated user.
|
|
||||||
Username string
|
|
||||||
// Pathname of shell to use for the emulated user.
|
|
||||||
Shell *container.Absolute
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *spAccountOp) toSystem(*outcomeStateSys, *hst.Config) error {
|
func (s spAccountOp) toSystem(state *outcomeStateSys, _ *hst.Config) error {
|
||||||
const fallbackUsername = "chronos"
|
const fallbackUsername = "chronos"
|
||||||
|
|
||||||
// do checks here to fail before fork/exec
|
// do checks here to fail before fork/exec
|
||||||
if s.Home == nil || s.Shell == nil {
|
if state.Container == nil || state.Container.Home == nil || state.Container.Shell == nil {
|
||||||
// unreachable
|
// unreachable
|
||||||
return syscall.ENOTRECOVERABLE
|
return syscall.ENOTRECOVERABLE
|
||||||
}
|
}
|
||||||
if s.Username == "" {
|
if state.Container.Username == "" {
|
||||||
s.Username = fallbackUsername
|
state.Container.Username = fallbackUsername
|
||||||
} else if !isValidUsername(s.Username) {
|
} else if !isValidUsername(state.Container.Username) {
|
||||||
return newWithMessage(fmt.Sprintf("invalid user name %q", s.Username))
|
return newWithMessage(fmt.Sprintf("invalid user name %q", state.Container.Username))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *spAccountOp) toContainer(state *outcomeStateParams) error {
|
func (s spAccountOp) toContainer(state *outcomeStateParams) error {
|
||||||
state.params.Dir = s.Home
|
state.params.Dir = state.Container.Home
|
||||||
state.env["HOME"] = s.Home.String()
|
state.env["HOME"] = state.Container.Home.String()
|
||||||
state.env["USER"] = s.Username
|
state.env["USER"] = state.Container.Username
|
||||||
state.env["SHELL"] = s.Shell.String()
|
state.env["SHELL"] = state.Container.Shell.String()
|
||||||
|
|
||||||
state.params.
|
state.params.
|
||||||
Place(container.AbsFHSEtc.Append("passwd"),
|
Place(container.AbsFHSEtc.Append("passwd"),
|
||||||
[]byte(s.Username+":x:"+
|
[]byte(state.Container.Username+":x:"+
|
||||||
state.mapuid.String()+":"+
|
state.mapuid.String()+":"+
|
||||||
state.mapgid.String()+
|
state.mapgid.String()+
|
||||||
":Hakurei:"+
|
":Hakurei:"+
|
||||||
s.Home.String()+":"+
|
state.Container.Home.String()+":"+
|
||||||
s.Shell.String()+"\n")).
|
state.Container.Shell.String()+"\n")).
|
||||||
Place(container.AbsFHSEtc.Append("group"),
|
Place(container.AbsFHSEtc.Append("group"),
|
||||||
[]byte("hakurei:x:"+state.mapgid.String()+":\n"))
|
[]byte("hakurei:x:"+state.mapgid.String()+":\n"))
|
||||||
|
|
||||||
|
@ -18,11 +18,6 @@ const varRunNscd = container.FHSVar + "run/nscd"
|
|||||||
// spParamsOp initialises unordered fields of [container.Params] and the optional root filesystem.
|
// spParamsOp initialises unordered fields of [container.Params] and the optional root filesystem.
|
||||||
// This outcomeOp is hardcoded to always run first.
|
// This outcomeOp is hardcoded to always run first.
|
||||||
type spParamsOp struct {
|
type spParamsOp struct {
|
||||||
// Copied from the [hst.Config] field of the same name.
|
|
||||||
Path *container.Absolute `json:"path,omitempty"`
|
|
||||||
// Copied from the [hst.Config] field of the same name.
|
|
||||||
Args []string `json:"args"`
|
|
||||||
|
|
||||||
// Value of $TERM, stored during toSystem.
|
// Value of $TERM, stored during toSystem.
|
||||||
Term string
|
Term string
|
||||||
// Whether $TERM is set, stored during toSystem.
|
// Whether $TERM is set, stored during toSystem.
|
||||||
@ -49,15 +44,15 @@ func (s *spParamsOp) toContainer(state *outcomeStateParams) error {
|
|||||||
state.params.HostNet = state.Container.HostNet
|
state.params.HostNet = state.Container.HostNet
|
||||||
state.params.HostAbstract = state.Container.HostAbstract
|
state.params.HostAbstract = state.Container.HostAbstract
|
||||||
|
|
||||||
if s.Path == nil {
|
if state.Container.Path == nil {
|
||||||
return newWithMessage("invalid program path")
|
return newWithMessage("invalid program path")
|
||||||
}
|
}
|
||||||
state.params.Path = s.Path
|
state.params.Path = state.Container.Path
|
||||||
|
|
||||||
if len(s.Args) == 0 {
|
if len(state.Container.Args) == 0 {
|
||||||
state.params.Args = []string{s.Path.String()}
|
state.params.Args = []string{state.Container.Path.String()}
|
||||||
} else {
|
} else {
|
||||||
state.params.Args = s.Args
|
state.params.Args = state.Container.Args
|
||||||
}
|
}
|
||||||
|
|
||||||
// the container is canceled when shim is requested to exit or receives an interrupt or termination signal;
|
// the container is canceled when shim is requested to exit or receives an interrupt or termination signal;
|
||||||
|
32
nixos.nix
32
nixos.nix
@ -105,27 +105,11 @@ in
|
|||||||
isGraphical = if app.gpu != null then app.gpu else app.enablements.wayland || app.enablements.x11;
|
isGraphical = if app.gpu != null then app.gpu else app.enablements.wayland || app.enablements.x11;
|
||||||
|
|
||||||
conf = {
|
conf = {
|
||||||
path =
|
|
||||||
if app.path == null then
|
|
||||||
pkgs.writeScript "${app.name}-start" ''
|
|
||||||
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
|
||||||
${script}
|
|
||||||
''
|
|
||||||
else
|
|
||||||
app.path;
|
|
||||||
args = if app.args == null then [ "${app.name}-start" ] else app.args;
|
|
||||||
|
|
||||||
inherit id;
|
inherit id;
|
||||||
|
inherit (app) identity groups enablements;
|
||||||
inherit (dbusConfig) session_bus system_bus;
|
inherit (dbusConfig) session_bus system_bus;
|
||||||
direct_wayland = app.insecureWayland;
|
direct_wayland = app.insecureWayland;
|
||||||
|
|
||||||
username = getsubname fid app.identity;
|
|
||||||
home = getsubhome fid app.identity;
|
|
||||||
|
|
||||||
inherit (cfg) shell;
|
|
||||||
inherit (app) identity groups enablements;
|
|
||||||
|
|
||||||
container = {
|
container = {
|
||||||
inherit (app)
|
inherit (app)
|
||||||
wait_delay
|
wait_delay
|
||||||
@ -219,6 +203,20 @@ in
|
|||||||
ensure = true;
|
ensure = true;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
username = getsubname fid app.identity;
|
||||||
|
inherit (cfg) shell;
|
||||||
|
home = getsubhome fid app.identity;
|
||||||
|
|
||||||
|
path =
|
||||||
|
if app.path == null then
|
||||||
|
pkgs.writeScript "${app.name}-start" ''
|
||||||
|
#!${pkgs.zsh}${pkgs.zsh.shellPath}
|
||||||
|
${script}
|
||||||
|
''
|
||||||
|
else
|
||||||
|
app.path;
|
||||||
|
args = if app.args == null then [ "${app.name}-start" ] else app.args;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -182,7 +182,6 @@
|
|||||||
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
|
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
|
||||||
(ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000000,gid=1000000")
|
(ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=1000000,gid=1000000")
|
||||||
(ent "/kvm" "/dev/kvm" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/kvm" "/dev/kvm" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/" "/run/nscd" "ro,nosuid,nodev,relatime" "tmpfs" "readonly" "ro,mode=755,uid=1000000,gid=1000000")
|
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=1000000,gid=1000000")
|
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=1000000,gid=1000000")
|
||||||
(ent "/" "/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=1000000,gid=1000000")
|
(ent "/" "/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=1000000,gid=1000000")
|
||||||
|
@ -61,14 +61,14 @@ def check_state(name, enablements):
|
|||||||
config = instance['config']
|
config = instance['config']
|
||||||
|
|
||||||
command = f"{name}-start"
|
command = f"{name}-start"
|
||||||
if not (config['path'].startswith("/nix/store/")) or not (config['path'].endswith(command)):
|
if not (config['container']['path'].startswith("/nix/store/")) or not (config['container']['path'].endswith(command)):
|
||||||
raise Exception(f"unexpected path {config['path']}")
|
raise Exception(f"unexpected path {config['path']}")
|
||||||
|
|
||||||
if len(config['args']) != 1 or config['args'][0] != command:
|
if len(config['container']['args']) != 1 or config['container']['args'][0] != command:
|
||||||
raise Exception(f"unexpected args {config['args']}")
|
raise Exception(f"unexpected args {config['args']}")
|
||||||
|
|
||||||
if config['enablements'] != enablements:
|
if config['enablements'] != enablements:
|
||||||
raise Exception(f"unexpected enablements {instance['config']['enablements']}")
|
raise Exception(f"unexpected enablements {config['enablements']['enablements']}")
|
||||||
|
|
||||||
|
|
||||||
def hakurei(command):
|
def hakurei(command):
|
||||||
@ -104,7 +104,7 @@ if denyOutputVerbose != "hsu: uid 1001 is not in the hsurc file\nhakurei: *canno
|
|||||||
raise Exception(f"unexpected deny verbose output:\n{denyOutputVerbose}")
|
raise Exception(f"unexpected deny verbose output:\n{denyOutputVerbose}")
|
||||||
|
|
||||||
# Verify timeout behaviour:
|
# Verify timeout behaviour:
|
||||||
machine.succeed('sudo -u alice -i hakurei-check-linger-timeout > /var/tmp/linger-stdout 2> /var/tmp/linger-stderr')
|
machine.succeed('sudo -u alice -i hakurei-check-linger-timeout > /var/tmp/linger-stdout 2> /var/tmp/linger-stderr || (cat /var/tmp/linger-stderr; false)')
|
||||||
linger_stdout = machine.succeed("cat /var/tmp/linger-stdout")
|
linger_stdout = machine.succeed("cat /var/tmp/linger-stdout")
|
||||||
linger_stderr = machine.succeed("cat /var/tmp/linger-stderr")
|
linger_stderr = machine.succeed("cat /var/tmp/linger-stderr")
|
||||||
if linger_stdout != "":
|
if linger_stdout != "":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user