app: integrate bwrap into environment setup
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
This commit is contained in:
parent
3ddfd76cdf
commit
662f2a9d2c
|
@ -24,9 +24,7 @@ type Config struct {
|
||||||
// ConfinementConfig defines fortified child's confinement
|
// ConfinementConfig defines fortified child's confinement
|
||||||
type ConfinementConfig struct {
|
type ConfinementConfig struct {
|
||||||
// bwrap sandbox confinement configuration
|
// bwrap sandbox confinement configuration
|
||||||
Sandbox *bwrap.Config `json:"sandbox"`
|
Sandbox *SandboxConfig `json:"sandbox"`
|
||||||
// mediated access to wayland socket
|
|
||||||
Wayland bool `json:"wayland"`
|
|
||||||
|
|
||||||
// reference to a system D-Bus proxy configuration,
|
// reference to a system D-Bus proxy configuration,
|
||||||
// nil value disables system bus proxy
|
// nil value disables system bus proxy
|
||||||
|
@ -38,3 +36,56 @@ type ConfinementConfig struct {
|
||||||
// child capability enablements
|
// child capability enablements
|
||||||
Enablements state.Enablements `json:"enablements"`
|
Enablements state.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"`
|
||||||
|
// do not run in new session
|
||||||
|
NoNewSession bool `json:"no_new_session,omitempty"`
|
||||||
|
// mediated access to wayland socket
|
||||||
|
Wayland bool `json:"wayland,omitempty"`
|
||||||
|
|
||||||
|
UID int `json:"uid,omitempty"`
|
||||||
|
GID int `json:"gid,omitempty"`
|
||||||
|
// final environment variables
|
||||||
|
Env map[string]string `json:"env"`
|
||||||
|
|
||||||
|
// paths made available within the sandbox
|
||||||
|
Bind [][2]string `json:"bind"`
|
||||||
|
// paths made available read-only within the sandbox
|
||||||
|
ROBind [][2]string `json:"ro-bind"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SandboxConfig) Bwrap() *bwrap.Config {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := &bwrap.Config{
|
||||||
|
Net: s.Net,
|
||||||
|
UserNS: s.UserNS,
|
||||||
|
Hostname: s.Hostname,
|
||||||
|
Clearenv: true,
|
||||||
|
SetEnv: s.Env,
|
||||||
|
Bind: s.Bind,
|
||||||
|
ROBind: s.ROBind,
|
||||||
|
Procfs: []string{"/proc"},
|
||||||
|
DevTmpfs: []string{"/dev"},
|
||||||
|
Mqueue: []string{"/dev/mqueue"},
|
||||||
|
NewSession: !s.NoNewSession,
|
||||||
|
DieWithParent: true,
|
||||||
|
}
|
||||||
|
if s.UID > 0 {
|
||||||
|
conf.UID = &s.UID
|
||||||
|
}
|
||||||
|
if s.GID > 0 {
|
||||||
|
conf.GID = &s.GID
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) {
|
func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) {
|
||||||
args = make([]string, 0, 9+len(a.seal.env))
|
args = make([]string, 0, 9+len(a.seal.sys.bwrap.SetEnv))
|
||||||
|
|
||||||
// shell --uid=$USER
|
// shell --uid=$USER
|
||||||
args = append(args, "shell", "--uid="+a.seal.sys.Username)
|
args = append(args, "shell", "--uid="+a.seal.sys.Username)
|
||||||
|
@ -20,12 +20,12 @@ func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// environ
|
// environ
|
||||||
envQ := make([]string, len(a.seal.env)+1)
|
envQ := make([]string, 0, len(a.seal.sys.bwrap.SetEnv)+1)
|
||||||
for i, e := range a.seal.env {
|
for k, v := range a.seal.sys.bwrap.SetEnv {
|
||||||
envQ[i] = "-E" + e
|
envQ = append(envQ, "-E"+k+"="+v)
|
||||||
}
|
}
|
||||||
// add shim payload to environment for shim path
|
// add shim payload to environment for shim path
|
||||||
envQ[len(a.seal.env)] = "-E" + shimEnv
|
envQ = append(envQ, "-E"+shimEnv)
|
||||||
args = append(args, envQ...)
|
args = append(args, envQ...)
|
||||||
|
|
||||||
// -- .host
|
// -- .host
|
||||||
|
@ -44,8 +44,8 @@ func (a *app) commandBuilderMachineCtl(shimEnv string) (args []string) {
|
||||||
|
|
||||||
// apply custom environment variables to activation environment
|
// apply custom environment variables to activation environment
|
||||||
innerCommand.WriteString("dbus-update-activation-environment --systemd")
|
innerCommand.WriteString("dbus-update-activation-environment --systemd")
|
||||||
for _, e := range a.seal.env {
|
for k := range a.seal.sys.bwrap.SetEnv {
|
||||||
innerCommand.WriteString(" " + strings.SplitN(e, "=", 2)[0])
|
innerCommand.WriteString(" " + k)
|
||||||
}
|
}
|
||||||
innerCommand.WriteString("; ")
|
innerCommand.WriteString("; ")
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/dbus"
|
"git.ophivana.moe/cat/fortify/dbus"
|
||||||
|
@ -63,12 +64,6 @@ func (a *app) Seal(config *Config) error {
|
||||||
// pass through config values
|
// pass through config values
|
||||||
seal.fid = config.ID
|
seal.fid = config.ID
|
||||||
seal.command = config.Command
|
seal.command = config.Command
|
||||||
seal.bwrap = config.Confinement.Sandbox
|
|
||||||
|
|
||||||
// create wayland client wait channel
|
|
||||||
if config.Confinement.Wayland {
|
|
||||||
seal.wlDone = make(chan struct{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// parses launch method text and looks up tool path
|
// parses launch method text and looks up tool path
|
||||||
switch config.Method {
|
switch config.Method {
|
||||||
|
@ -115,6 +110,65 @@ func (a *app) Seal(config *Config) error {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
seal.sys.User = u
|
seal.sys.User = u
|
||||||
|
seal.sys.runtime = path.Join("/run/user", u.Uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// map sandbox config to bwrap
|
||||||
|
if config.Confinement.Sandbox == nil {
|
||||||
|
verbose.Println("sandbox configuration not supplied, PROCEED WITH CAUTION")
|
||||||
|
|
||||||
|
// permissive defaults
|
||||||
|
conf := &SandboxConfig{
|
||||||
|
UserNS: true,
|
||||||
|
Net: true,
|
||||||
|
NoNewSession: true,
|
||||||
|
}
|
||||||
|
// bind entries in /
|
||||||
|
if d, err := os.ReadDir("/"); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
b := make([][2]string, 0, len(d))
|
||||||
|
for _, ent := range d {
|
||||||
|
name := ent.Name()
|
||||||
|
switch name {
|
||||||
|
case "proc":
|
||||||
|
case "dev":
|
||||||
|
case "run":
|
||||||
|
default:
|
||||||
|
p := "/" + name
|
||||||
|
b = append(b, [2]string{p, p})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf.Bind = append(conf.Bind, b...)
|
||||||
|
}
|
||||||
|
// bind entries in /run
|
||||||
|
if d, err := os.ReadDir("/run"); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
b := make([][2]string, 0, len(d))
|
||||||
|
for _, ent := range d {
|
||||||
|
name := ent.Name()
|
||||||
|
switch name {
|
||||||
|
case "user":
|
||||||
|
case "dbus":
|
||||||
|
default:
|
||||||
|
p := "/run/" + name
|
||||||
|
b = append(b, [2]string{p, p})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conf.Bind = append(conf.Bind, b...)
|
||||||
|
}
|
||||||
|
config.Confinement.Sandbox = conf
|
||||||
|
}
|
||||||
|
seal.sys.bwrap = config.Confinement.Sandbox.Bwrap()
|
||||||
|
if seal.sys.bwrap.SetEnv == nil {
|
||||||
|
seal.sys.bwrap.SetEnv = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create wayland client wait channel if mediated wayland is enabled
|
||||||
|
// this channel being set enables mediated wayland setup later on
|
||||||
|
if config.Confinement.Sandbox.Wayland {
|
||||||
|
seal.wlDone = make(chan struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// open process state store
|
// open process state store
|
||||||
|
|
|
@ -63,10 +63,14 @@ func (seal *appSeal) shareDBus(config [2]*dbus.Config) error {
|
||||||
seal.sys.dbusAddr = &[2][2]string{sessionBus, systemBus}
|
seal.sys.dbusAddr = &[2][2]string{sessionBus, systemBus}
|
||||||
|
|
||||||
// share proxy sockets
|
// share proxy sockets
|
||||||
seal.appendEnv(dbusSessionBusAddress, "unix:path="+sessionBus[1])
|
sessionInner := path.Join(seal.sys.runtime, "bus")
|
||||||
|
seal.sys.setEnv(dbusSessionBusAddress, "unix:path="+sessionInner)
|
||||||
|
seal.sys.bind(sessionBus[1], sessionInner, true)
|
||||||
seal.sys.updatePerm(sessionBus[1], acl.Read, acl.Write)
|
seal.sys.updatePerm(sessionBus[1], acl.Read, acl.Write)
|
||||||
if seal.sys.dbusSystem {
|
if seal.sys.dbusSystem {
|
||||||
seal.appendEnv(dbusSystemBusAddress, "unix:path="+systemBus[1])
|
systemInner := "/run/dbus/system_bus_socket"
|
||||||
|
seal.sys.setEnv(dbusSystemBusAddress, "unix:path="+systemInner)
|
||||||
|
seal.sys.bind(systemBus[1], systemInner, true)
|
||||||
seal.sys.updatePerm(systemBus[1], acl.Read, acl.Write)
|
seal.sys.updatePerm(systemBus[1], acl.Read, acl.Write)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ type ErrDisplayEnv BaseError
|
||||||
func (seal *appSeal) shareDisplay() error {
|
func (seal *appSeal) shareDisplay() error {
|
||||||
// pass $TERM to launcher
|
// pass $TERM to launcher
|
||||||
if t, ok := os.LookupEnv(term); ok {
|
if t, ok := os.LookupEnv(term); ok {
|
||||||
seal.appendEnv(term, t)
|
seal.sys.setEnv(term, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up wayland
|
// set up wayland
|
||||||
|
@ -38,8 +38,10 @@ func (seal *appSeal) shareDisplay() error {
|
||||||
// hardlink wayland socket
|
// hardlink wayland socket
|
||||||
wp := path.Join(seal.RuntimePath, wd)
|
wp := path.Join(seal.RuntimePath, wd)
|
||||||
wpi := path.Join(seal.shareLocal, "wayland")
|
wpi := path.Join(seal.shareLocal, "wayland")
|
||||||
|
w := path.Join(seal.sys.runtime, "wayland-0")
|
||||||
seal.sys.link(wp, wpi)
|
seal.sys.link(wp, wpi)
|
||||||
seal.appendEnv(waylandDisplay, wpi)
|
seal.sys.setEnv(waylandDisplay, w)
|
||||||
|
seal.sys.bind(wpi, w, true)
|
||||||
|
|
||||||
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
// ensure Wayland socket ACL (e.g. `/run/user/%d/wayland-%d`)
|
||||||
seal.sys.updatePermTag(state.EnableWayland, wp, acl.Read, acl.Write, acl.Execute)
|
seal.sys.updatePermTag(state.EnableWayland, wp, acl.Read, acl.Write, acl.Execute)
|
||||||
|
@ -56,7 +58,8 @@ func (seal *appSeal) shareDisplay() error {
|
||||||
return (*ErrDisplayEnv)(wrapError(ErrXDisplay, "DISPLAY is not set"))
|
return (*ErrDisplayEnv)(wrapError(ErrXDisplay, "DISPLAY is not set"))
|
||||||
} else {
|
} else {
|
||||||
seal.sys.changeHosts(seal.sys.Username)
|
seal.sys.changeHosts(seal.sys.Username)
|
||||||
seal.appendEnv(display, d)
|
seal.sys.setEnv(display, d)
|
||||||
|
seal.sys.bind("/tmp/.X11-unix", "/tmp/.X11-unix", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,15 +63,17 @@ func (seal *appSeal) sharePulse() error {
|
||||||
|
|
||||||
// hard link pulse socket into target-executable share
|
// hard link pulse socket into target-executable share
|
||||||
psi := path.Join(seal.shareLocal, "pulse")
|
psi := path.Join(seal.shareLocal, "pulse")
|
||||||
|
p := path.Join(seal.sys.runtime, "pulse", "native")
|
||||||
seal.sys.link(ps, psi)
|
seal.sys.link(ps, psi)
|
||||||
seal.appendEnv(pulseServer, "unix:"+psi)
|
seal.sys.bind(psi, p, true)
|
||||||
|
seal.sys.setEnv(pulseServer, "unix:"+p)
|
||||||
|
|
||||||
// publish current user's pulse cookie for target user
|
// publish current user's pulse cookie for target user
|
||||||
if src, err := discoverPulseCookie(); err != nil {
|
if src, err := discoverPulseCookie(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
dst := path.Join(seal.share, "pulse-cookie")
|
dst := path.Join(seal.share, "pulse-cookie")
|
||||||
seal.appendEnv(pulseCookie, dst)
|
seal.sys.setEnv(pulseCookie, dst)
|
||||||
seal.sys.copyFile(dst, src)
|
seal.sys.copyFile(dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.ophivana.moe/cat/fortify/acl"
|
"git.ophivana.moe/cat/fortify/acl"
|
||||||
|
"git.ophivana.moe/cat/fortify/helper/bwrap"
|
||||||
"git.ophivana.moe/cat/fortify/internal/state"
|
"git.ophivana.moe/cat/fortify/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,9 +21,25 @@ const (
|
||||||
func (seal *appSeal) shareRuntime() {
|
func (seal *appSeal) shareRuntime() {
|
||||||
// look up shell
|
// look up shell
|
||||||
if s, ok := os.LookupEnv(shell); ok {
|
if s, ok := os.LookupEnv(shell); ok {
|
||||||
seal.appendEnv(shell, s)
|
seal.sys.setEnv(shell, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mount tmpfs on inner runtime (e.g. `/run/user/%d`)
|
||||||
|
seal.sys.bwrap.Tmpfs = append(seal.sys.bwrap.Tmpfs,
|
||||||
|
bwrap.PermConfig[bwrap.TmpfsConfig]{
|
||||||
|
Path: bwrap.TmpfsConfig{
|
||||||
|
Size: 1 * 1024 * 1024,
|
||||||
|
Dir: "/run/user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bwrap.PermConfig[bwrap.TmpfsConfig]{
|
||||||
|
Path: bwrap.TmpfsConfig{
|
||||||
|
Size: 8 * 1024 * 1024,
|
||||||
|
Dir: seal.sys.runtime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// ensure RunDir (e.g. `/run/user/%d/fortify`)
|
// ensure RunDir (e.g. `/run/user/%d/fortify`)
|
||||||
seal.sys.ensure(seal.RunDirPath, 0700)
|
seal.sys.ensure(seal.RunDirPath, 0700)
|
||||||
seal.sys.updatePermTag(state.EnableLength, seal.RunDirPath, acl.Execute)
|
seal.sys.updatePermTag(state.EnableLength, seal.RunDirPath, acl.Execute)
|
||||||
|
@ -57,9 +74,9 @@ func (seal *appSeal) shareRuntimeChild() string {
|
||||||
seal.sys.updatePermTag(state.EnableLength, targetRuntime, acl.Read, acl.Write, acl.Execute)
|
seal.sys.updatePermTag(state.EnableLength, targetRuntime, acl.Read, acl.Write, acl.Execute)
|
||||||
|
|
||||||
// point to ensured runtime path
|
// point to ensured runtime path
|
||||||
seal.appendEnv(xdgRuntimeDir, targetRuntime)
|
seal.sys.setEnv(xdgRuntimeDir, targetRuntime)
|
||||||
seal.appendEnv(xdgSessionClass, "user")
|
seal.sys.setEnv(xdgSessionClass, "user")
|
||||||
seal.appendEnv(xdgSessionType, "tty")
|
seal.sys.setEnv(xdgSessionType, "tty")
|
||||||
|
|
||||||
return targetRuntime
|
return targetRuntime
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,9 +72,8 @@ func (a *app) Start() error {
|
||||||
|
|
||||||
if wls, err := shim.ServeConfig(confSockPath, &shim.Payload{
|
if wls, err := shim.ServeConfig(confSockPath, &shim.Payload{
|
||||||
Argv: a.seal.command,
|
Argv: a.seal.command,
|
||||||
Env: a.seal.env,
|
|
||||||
Exec: e,
|
Exec: e,
|
||||||
Bwrap: a.seal.bwrap,
|
Bwrap: a.seal.sys.bwrap,
|
||||||
WL: a.seal.wlDone != nil,
|
WL: a.seal.wlDone != nil,
|
||||||
|
|
||||||
Verbose: verbose.Get(),
|
Verbose: verbose.Get(),
|
||||||
|
|
|
@ -20,19 +20,16 @@ import (
|
||||||
type appSeal struct {
|
type appSeal struct {
|
||||||
// application unique identifier
|
// application unique identifier
|
||||||
id *appID
|
id *appID
|
||||||
// bwrap config
|
|
||||||
bwrap *bwrap.Config
|
|
||||||
// wayland socket path if mediated wayland is enabled
|
// wayland socket path if mediated wayland is enabled
|
||||||
wl string
|
wl string
|
||||||
// wait for wayland client to exit if mediated wayland is enabled
|
// wait for wayland client to exit if mediated wayland is enabled,
|
||||||
|
// (wlDone == nil) determines whether mediated wayland setup is performed
|
||||||
wlDone chan struct{}
|
wlDone chan struct{}
|
||||||
|
|
||||||
// freedesktop application ID
|
// freedesktop application ID
|
||||||
fid string
|
fid string
|
||||||
// argv to start process with in the final confined environment
|
// argv to start process with in the final confined environment
|
||||||
command []string
|
command []string
|
||||||
// environment variables of fortified process
|
|
||||||
env []string
|
|
||||||
// persistent process state store
|
// persistent process state store
|
||||||
store state.Store
|
store state.Store
|
||||||
|
|
||||||
|
@ -59,13 +56,10 @@ type appSeal struct {
|
||||||
// protected by upstream mutex
|
// protected by upstream mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// appendEnv appends an environment variable for the child process
|
|
||||||
func (seal *appSeal) appendEnv(k, v string) {
|
|
||||||
seal.env = append(seal.env, k+"="+v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// appSealTx contains the system-level component of the app seal
|
// appSealTx contains the system-level component of the app seal
|
||||||
type appSealTx struct {
|
type appSealTx struct {
|
||||||
|
bwrap *bwrap.Config
|
||||||
|
|
||||||
// reference to D-Bus proxy instance, nil if disabled
|
// reference to D-Bus proxy instance, nil if disabled
|
||||||
dbus *dbus.Proxy
|
dbus *dbus.Proxy
|
||||||
// notification from goroutine waiting for dbus.Proxy
|
// notification from goroutine waiting for dbus.Proxy
|
||||||
|
@ -86,6 +80,8 @@ type appSealTx struct {
|
||||||
// dst, src pairs of temporarily hard linked files
|
// dst, src pairs of temporarily hard linked files
|
||||||
hardlinks [][2]string
|
hardlinks [][2]string
|
||||||
|
|
||||||
|
// default formatted XDG_RUNTIME_DIR of User
|
||||||
|
runtime string
|
||||||
// sealed path to fortify executable, used by shim
|
// sealed path to fortify executable, used by shim
|
||||||
executable string
|
executable string
|
||||||
// target user UID as an integer
|
// target user UID as an integer
|
||||||
|
@ -107,6 +103,20 @@ type appEnsureEntry struct {
|
||||||
remove bool
|
remove bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setEnv sets an environment variable for the child process
|
||||||
|
func (tx *appSealTx) setEnv(k, v string) {
|
||||||
|
tx.bwrap.SetEnv[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// bind mounts a directory within the sandbox
|
||||||
|
func (tx *appSealTx) bind(src, dest string, ro bool) {
|
||||||
|
if !ro {
|
||||||
|
tx.bwrap.Bind = append(tx.bwrap.Bind, [2]string{src, dest})
|
||||||
|
} else {
|
||||||
|
tx.bwrap.ROBind = append(tx.bwrap.ROBind, [2]string{src, dest})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ensure appends a directory ensure action
|
// ensure appends a directory ensure action
|
||||||
func (tx *appSealTx) ensure(path string, perm os.FileMode) {
|
func (tx *appSealTx) ensure(path string, perm os.FileMode) {
|
||||||
tx.mkdir = append(tx.mkdir, appEnsureEntry{path, perm, false})
|
tx.mkdir = append(tx.mkdir, appEnsureEntry{path, perm, false})
|
||||||
|
@ -171,6 +181,7 @@ func (tx *appSealTx) changeHosts(username string) {
|
||||||
func (tx *appSealTx) copyFile(dst, src string) {
|
func (tx *appSealTx) copyFile(dst, src string) {
|
||||||
tx.tmpfiles = append(tx.tmpfiles, [2]string{dst, src})
|
tx.tmpfiles = append(tx.tmpfiles, [2]string{dst, src})
|
||||||
tx.updatePerm(dst, acl.Read)
|
tx.updatePerm(dst, acl.Read)
|
||||||
|
tx.bind(dst, dst, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// link appends a hardlink action
|
// link appends a hardlink action
|
||||||
|
@ -194,7 +205,7 @@ func (tx *appSealTx) commit() error {
|
||||||
}
|
}
|
||||||
tx.complete = true
|
tx.complete = true
|
||||||
|
|
||||||
txp := &appSealTx{User: tx.User}
|
txp := &appSealTx{User: tx.User, bwrap: &bwrap.Config{SetEnv: make(map[string]string)}}
|
||||||
defer func() {
|
defer func() {
|
||||||
// rollback partial commit
|
// rollback partial commit
|
||||||
if txp != nil {
|
if txp != nil {
|
||||||
|
|
Loading…
Reference in New Issue