diff options
| author | Ophestra Umiker <cat@ophivana.moe> | 2024-12-18 13:45:55 +0900 |
|---|---|---|
| committer | Ophestra Umiker <cat@ophivana.moe> | 2024-12-18 13:45:55 +0900 |
| commit | b752ec44689aa5a3a0f3b9672b218691f161c7c1 (patch) | |
| tree | b514cb0c247f8deaa9feba84d9f8944623911f5d /fipc | |
| parent | 5d00805a7c775382516e05b5b01e58df651408ab (diff) | |
fipc: export config struct
Also store full config as part of state.
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'fipc')
| -rw-r--r-- | fipc/config.go | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/fipc/config.go b/fipc/config.go new file mode 100644 index 00000000..472d22f6 --- /dev/null +++ b/fipc/config.go @@ -0,0 +1,242 @@ +package fipc + +import ( + "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" +) + +const fTmp = "/fortify" + +// Config is used to seal an *App +type Config struct { + // D-Bus application ID + ID string `json:"id"` + // value passed through to the child process as its argv + Command []string `json:"command"` + + // child confinement configuration + Confinement ConfinementConfig `json:"confinement"` +} + +// ConfinementConfig defines fortified child's confinement +type ConfinementConfig struct { + // numerical application id, determines uid in the init namespace + AppID int `json:"app_id"` + // list of supplementary groups to inherit + Groups []string `json:"groups"` + // passwd username in the sandbox, defaults to chronos + Username string `json:"username,omitempty"` + // home directory in sandbox, empty for outer + Inner string `json:"home_inner"` + // home directory in init namespace + Outer string `json:"home"` + // bwrap sandbox confinement configuration + Sandbox *SandboxConfig `json:"sandbox"` + + // reference to a system D-Bus proxy configuration, + // nil value disables system bus proxy + SystemBus *dbus.Config `json:"system_bus,omitempty"` + // reference to a session D-Bus proxy configuration, + // nil value makes session bus proxy assume built-in defaults + SessionBus *dbus.Config `json:"session_bus,omitempty"` + + // child capability enablements + Enablements system.Enablements `json:"enablements"` +} + +// SandboxConfig describes resources made available to the sandbox. +type SandboxConfig struct { + // unix hostname within sandbox + Hostname string `json:"hostname,omitempty"` + // userns availability within sandbox + UserNS bool `json:"userns,omitempty"` + // share net namespace + Net bool `json:"net,omitempty"` + // share all devices + Dev bool `json:"dev,omitempty"` + // do not run in new session + NoNewSession bool `json:"no_new_session,omitempty"` + // map target user uid to privileged user uid in the user namespace + MapRealUID bool `json:"map_real_uid"` + // direct access to wayland socket + DirectWayland bool `json:"direct_wayland,omitempty"` + + // final environment variables + Env map[string]string `json:"env"` + // sandbox host filesystem access + 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"` +} + +type FilesystemConfig struct { + // mount point in sandbox, same as src if empty + Dst string `json:"dst,omitempty"` + // host filesystem path to make available to sandbox + Src string `json:"src"` + // write access + Write bool `json:"write,omitempty"` + // device access + Device bool `json:"dev,omitempty"` + // exit if unable to share + Must bool `json:"require,omitempty"` +} + +// 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(os linux.System) (*bwrap.Config, error) { + if s == nil { + return nil, errors.New("nil sandbox config") + } + + var uid int + if !s.MapRealUID { + uid = 65534 + } else { + uid = os.Geteuid() + } + + conf := (&bwrap.Config{ + Net: s.Net, + UserNS: s.UserNS, + Hostname: s.Hostname, + Clearenv: true, + SetEnv: s.Env, + NewSession: !s.NoNewSession, + DieWithParent: true, + AsInit: true, + + // initialise map + Chmod: make(bwrap.ChmodConfig), + }). + SetUID(uid).SetGID(uid). + Procfs("/proc"). + Tmpfs(fTmp, 4*1024) + + if !s.Dev { + conf.DevTmpfs("/dev").Mqueue("/dev/mqueue") + } else { + conf.Bind("/dev", "/dev", false, true, true) + } + + if !s.AutoEtc { + conf.Dir("/etc") + } + + for _, c := range s.Filesystem { + if c == nil { + continue + } + src := c.Src + dest := c.Dst + if c.Dst == "" { + dest = c.Src + } + conf.Bind(src, dest, !c.Must, c.Write, c.Device) + } + + for _, l := range s.Link { + conf.Symlink(l[0], l[1]) + } + + if s.AutoEtc { + conf.Bind("/etc", fTmp+"/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(fTmp+"/etc/"+name, "/etc/"+name) + } + } + } + } + + return conf, nil +} + +// Template returns a fully populated instance of Config. +func Template() *Config { + return &Config{ + ID: "org.chromium.Chromium", + Command: []string{ + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland", + }, + Confinement: ConfinementConfig{ + AppID: 9, + Groups: []string{"video"}, + Username: "chronos", + Outer: "/var/lib/persist/home/org.chromium.Chromium", + Inner: "/var/lib/fortify", + Sandbox: &SandboxConfig{ + Hostname: "localhost", + UserNS: true, + Net: true, + NoNewSession: true, + MapRealUID: true, + Dev: true, + DirectWayland: false, + // example API credentials pulled from Google Chrome + // DO NOT USE THESE IN A REAL BROWSER + Env: map[string]string{ + "GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", + "GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", + "GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT", + }, + Filesystem: []*FilesystemConfig{ + {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{{"/run/user/65534", "/run/user/150"}}, + AutoEtc: true, + Override: []string{"/var/run/nscd"}, + }, + SystemBus: &dbus.Config{ + See: nil, + Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, + Own: nil, + Call: nil, + Broadcast: nil, + Log: false, + Filter: true, + }, + SessionBus: &dbus.Config{ + See: nil, + Talk: []string{"org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver", + "org.freedesktop.secrets", "org.kde.kwalletd5", "org.kde.kwalletd6", "org.gnome.SessionManager"}, + Own: []string{"org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*", + "org.mpris.MediaPlayer2.chromium.*"}, + Call: map[string]string{"org.freedesktop.portal.*": "*"}, + Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, + Log: false, + Filter: true, + }, + Enablements: system.EWayland.Mask() | system.EDBus.Mask() | system.EPulse.Mask(), + }, + } +} |
