diff --git a/internal/app/app_nixos_test.go b/internal/app/app_nixos_test.go index 68cb5fc..c5a31a6 100644 --- a/internal/app/app_nixos_test.go +++ b/internal/app/app_nixos_test.go @@ -58,7 +58,6 @@ var testCasesNixos = []sealTestCase{ Tmpfs("/dev/fortify", 4096). Bind("/bin", "/bin", false, true). Bind("/boot", "/boot", false, true). - Bind("/etc", "/dev/fortify/etc"). Bind("/home", "/home", false, true). Bind("/lib", "/lib", false, true). Bind("/lib64", "/lib64", false, true). @@ -102,6 +101,7 @@ var testCasesNixos = []sealTestCase{ Bind("/run/wrappers", "/run/wrappers", false, true). Bind("/run/zed.pid", "/run/zed.pid", false, true). Bind("/run/zed.state", "/run/zed.state", false, true). + Bind("/etc", "/dev/fortify/etc"). Symlink("/dev/fortify/etc/alsa", "/etc/alsa"). Symlink("/dev/fortify/etc/bashrc", "/etc/bashrc"). Symlink("/dev/fortify/etc/binfmt.d", "/etc/binfmt.d"). @@ -308,7 +308,6 @@ var testCasesNixos = []sealTestCase{ Tmpfs("/dev/fortify", 4096). Bind("/bin", "/bin", false, true). Bind("/boot", "/boot", false, true). - Bind("/etc", "/dev/fortify/etc"). Bind("/home", "/home", false, true). Bind("/lib", "/lib", false, true). Bind("/lib64", "/lib64", false, true). @@ -353,6 +352,7 @@ var testCasesNixos = []sealTestCase{ Bind("/run/zed.pid", "/run/zed.pid", false, true). Bind("/run/zed.state", "/run/zed.state", false, true). Bind("/dev/dri", "/dev/dri", true, true, true). + Bind("/etc", "/dev/fortify/etc"). Symlink("/dev/fortify/etc/alsa", "/etc/alsa"). Symlink("/dev/fortify/etc/bashrc", "/etc/bashrc"). Symlink("/dev/fortify/etc/binfmt.d", "/etc/binfmt.d"). diff --git a/internal/app/config.go b/internal/app/config.go index da3f516..cea5060 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -1,10 +1,11 @@ package app import ( - "os" + "errors" "git.ophivana.moe/security/fortify/dbus" "git.ophivana.moe/security/fortify/helper/bwrap" + "git.ophivana.moe/security/fortify/internal/linux" "git.ophivana.moe/security/fortify/internal/system" ) @@ -60,6 +61,8 @@ type SandboxConfig struct { Filesystem []*FilesystemConfig `json:"filesystem"` // symlinks created inside the sandbox Link [][2]string `json:"symlink"` + // automatically set up /etc symlinks + AutoEtc bool `json:"auto_etc"` // paths to override by mounting tmpfs over them Override []string `json:"override"` } @@ -79,13 +82,16 @@ type FilesystemConfig struct { // Bwrap returns the address of the corresponding bwrap.Config to s. // Note that remaining tmpfs entries must be queued by the caller prior to launch. -func (s *SandboxConfig) Bwrap(uid int) *bwrap.Config { +func (s *SandboxConfig) Bwrap(os linux.System) (*bwrap.Config, error) { if s == nil { - return nil + return nil, errors.New("nil sandbox config") } + var uid int if !s.UseRealUID { uid = 65534 + } else { + uid = os.Geteuid() } conf := (&bwrap.Config{ @@ -99,12 +105,16 @@ func (s *SandboxConfig) Bwrap(uid int) *bwrap.Config { AsInit: true, // initialise map - Chmod: make(map[string]os.FileMode), + Chmod: make(bwrap.ChmodConfig), }). SetUID(uid).SetGID(uid). Procfs("/proc").DevTmpfs("/dev").Mqueue("/dev/mqueue"). Tmpfs("/dev/fortify", 4*1024) + if !s.AutoEtc { + conf.Dir("/etc") + } + for _, c := range s.Filesystem { if c == nil { continue @@ -121,7 +131,29 @@ func (s *SandboxConfig) Bwrap(uid int) *bwrap.Config { conf.Symlink(l[0], l[1]) } - return conf + if s.AutoEtc { + conf.Bind("/etc", "/dev/fortify/etc") + + // link host /etc contents to prevent passwd/group from being overwritten + if d, err := os.ReadDir("/etc"); err != nil { + return nil, err + } else { + for _, ent := range d { + name := ent.Name() + switch name { + case "passwd": + case "group": + + case "mtab": + conf.Symlink("/proc/mounts", "/etc/"+name) + default: + conf.Symlink("/dev/fortify/etc/"+name, "/etc/"+name) + } + } + } + } + + return conf, nil } // Template returns a fully populated instance of Config. @@ -153,12 +185,15 @@ func Template() *Config { "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT", }, Filesystem: []*FilesystemConfig{ - {Src: "/nix"}, - {Src: "/storage/emulated/0", Write: true, Must: true}, - {Src: "/data/user/0", Dst: "/data/data", Write: true, Must: true}, - {Src: "/var/tmp", Write: true}, + {Src: "/nix/store"}, + {Src: "/run/current-system"}, + {Src: "/run/opengl-driver"}, + {Src: "/var/db/nix-channels"}, + {Src: "/home/chronos", Write: true, Must: true}, + {Src: "/dev/dri", Device: true}, }, - Link: [][2]string{{"/dev/fortify/etc", "/etc"}}, + Link: [][2]string{{"/run/user/65534", "/run/user/150"}}, + AutoEtc: true, Override: []string{"/var/run/nscd"}, }, SystemBus: &dbus.Config{ diff --git a/internal/app/seal.go b/internal/app/seal.go index aaf8a2d..0b60920 100644 --- a/internal/app/seal.go +++ b/internal/app/seal.go @@ -159,6 +159,7 @@ func (a *app) Seal(config *Config) error { UserNS: true, Net: true, NoNewSession: true, + AutoEtc: true, } // bind entries in / if d, err := a.os.ReadDir("/"); err != nil { @@ -173,9 +174,8 @@ func (a *app) Seal(config *Config) error { case "/run": case "/tmp": case "/mnt": - case "/etc": - b = append(b, &FilesystemConfig{Src: p, Dst: "/dev/fortify/etc", Write: false, Must: true}) + default: b = append(b, &FilesystemConfig{Src: p, Write: true, Must: true}) } @@ -208,35 +208,14 @@ func (a *app) Seal(config *Config) error { if config.Confinement.Enablements.Has(system.EX11) || config.Confinement.Enablements.Has(system.EWayland) { conf.Filesystem = append(conf.Filesystem, &FilesystemConfig{Src: "/dev/dri", Device: true}) } - // link host /etc to prevent passwd/group from being overwritten - if d, err := a.os.ReadDir("/etc"); err != nil { - return err - } else { - b := make([][2]string, 0, len(d)) - for _, ent := range d { - name := ent.Name() - switch name { - case "passwd": - case "group": - - case "mtab": - b = append(b, [2]string{ - "/proc/mounts", - "/etc/" + name, - }) - default: - b = append(b, [2]string{ - "/dev/fortify/etc/" + name, - "/etc/" + name, - }) - } - } - conf.Link = append(conf.Link, b...) - } config.Confinement.Sandbox = conf } - seal.sys.bwrap = config.Confinement.Sandbox.Bwrap(a.os.Geteuid()) + if b, err := config.Confinement.Sandbox.Bwrap(a.os); err != nil { + return err + } else { + seal.sys.bwrap = b + } seal.sys.override = config.Confinement.Sandbox.Override if seal.sys.bwrap.SetEnv == nil { seal.sys.bwrap.SetEnv = make(map[string]string)