diff --git a/cmd/oz-setup/main.go b/cmd/oz-setup/main.go index 8c909d4..dd0ef09 100644 --- a/cmd/oz-setup/main.go +++ b/cmd/oz-setup/main.go @@ -43,6 +43,13 @@ func main() { }, } + flagsForce := []cli.Flag{ + cli.BoolFlag{ + Name: "force, f", + Usage: "Force the command to run through non fatal errors", + }, + } + app.Commands = []cli.Command{ { Name: "config", @@ -64,13 +71,13 @@ func main() { Name: "install", Usage: "install binary diversion for a program", Action: handleInstall, - Flags: flagsHookMode, + Flags: append(flagsForce, flagsHookMode...), }, { Name: "remove", Usage: "remove a binary diversion for a program", Action: handleRemove, - Flags: flagsHookMode, + Flags: append(flagsForce, flagsHookMode...), }, { Name: "status", @@ -147,8 +154,8 @@ func handleConfigshow(c *cli.Context) { fmt.Printf(hfmt, "Config file", "Not found - using defaults") } - for i := 0; i < len(fmt.Sprintf(sfmt, "", "")); i++ { - fmt.Print("=") + for i := 0; i < len(fmt.Sprintf(sfmt, "", ""))+2; i++ { + fmt.Print("#") } fmt.Println("") @@ -180,39 +187,52 @@ func handleInstall(c *cli.Context) { return // For clarity } - isInstalled, err := isDivertInstalled(OzProfile.Path) - if err != nil { - fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err) - os.Exit(1) - } - if isInstalled == true { - fmt.Println("Divert already installed for ", OzProfile.Path) - os.Exit(0) - } + divertInstall := func(cpath string) { + isInstalled, err := isDivertInstalled(cpath) + if err != nil { + fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err) + os.Exit(1) + } + if isInstalled == true { + fmt.Println("Divert already installed for ", cpath) + if c.Bool("force") { + return + } + os.Exit(0) + } - dpkgArgs := []string{ - "--add", - "--package", - "oz", - "--rename", - "--divert", - getBinaryPath(OzProfile.Path), - OzProfile.Path, - } + dpkgArgs := []string{ + "--add", + "--package", + "oz", + "--rename", + "--divert", + getBinaryPath(cpath), + cpath, + } - _, err = exec.Command(PathDpkgDivert, dpkgArgs...).Output() - if err != nil { - fmt.Fprintf(os.Stderr, "Dpkg divert command `%s %+s` failed: %s", PathDpkgDivert, dpkgArgs, err) - os.Exit(1) + _, err = exec.Command(PathDpkgDivert, dpkgArgs...).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "Dpkg divert command `%s %+s` failed: %s", PathDpkgDivert, dpkgArgs, err) + os.Exit(1) + } + + err = syscall.Symlink(OzConfig.ClientPath, cpath) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to create symlink %s", err) + os.Exit(1) + } + + fmt.Printf("Successfully installed Oz sandbox for: %s.\n", cpath) } - err = syscall.Symlink(OzConfig.ClientPath, OzProfile.Path) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create symlink %s", err) - os.Exit(1) + paths := append([]string{}, OzProfile.Path) + paths = append(paths, OzProfile.Paths...) + for _, pp := range paths { + divertInstall(pp) } - fmt.Printf("Successfully installed Oz sandbox for: %s.\n", OzProfile.Path) + fmt.Printf("Successfully installed Oz sandbox for: %s.\n", OzProfile.Name) } func handleRemove(c *cli.Context) { @@ -229,33 +249,46 @@ func handleRemove(c *cli.Context) { return // For clarity } - isInstalled, err := isDivertInstalled(OzProfile.Path) - if err != nil { - fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err) - os.Exit(1) - } - if isInstalled == false { - fmt.Println("Divert is not installed for ", OzProfile.Path) - os.Exit(0) - } + divertRemove := func(cpath string) { + isInstalled, err := isDivertInstalled(cpath) + if err != nil { + fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err) + os.Exit(1) + } + if isInstalled == false { + fmt.Println("Divert is not installed for ", cpath) + if c.Bool("force") { + return + } + os.Exit(0) + } + + os.Remove(cpath) + + dpkgArgs := []string{ + "--rename", + "--package", + "oz", + "--remove", + cpath, + } - os.Remove(OzProfile.Path) + _, err = exec.Command(PathDpkgDivert, dpkgArgs...).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "Dpkg divert command `%s %+s` failed: %s", PathDpkgDivert, dpkgArgs, err) + os.Exit(1) + } - dpkgArgs := []string{ - "--rename", - "--package", - "oz", - "--remove", - OzProfile.Path, + fmt.Printf("Successfully remove jail for: %s.\n", cpath) } - _, err = exec.Command(PathDpkgDivert, dpkgArgs...).Output() - if err != nil { - fmt.Fprintf(os.Stderr, "Dpkg divert command `%s %+s` failed: %s", PathDpkgDivert, dpkgArgs, err) - os.Exit(1) + paths := append([]string{}, OzProfile.Path) + paths = append(paths, OzProfile.Paths...) + for _, pp := range paths { + divertRemove(pp) } - fmt.Printf("Successfully remove jail for: %s.\n", OzProfile.Path) + fmt.Printf("Successfully remove jail for: %s.\n", OzProfile.Name) } func handleStatus(c *cli.Context) { @@ -263,7 +296,7 @@ func handleStatus(c *cli.Context) { pname := c.Args()[0] OzProfile, err := loadProfile(pname, OzConfig.ProfileDir) if err != nil || OzProfile == nil { - fmt.Fprintf(os.Stderr, "Unable to load profiles (%s).\n", err) + fmt.Fprintf(os.Stderr, "Unable to load profiles (%s): %v.\n", pname, err) os.Exit(1) } @@ -272,17 +305,25 @@ func handleStatus(c *cli.Context) { os.Exit(1) } - isInstalled, err := isDivertInstalled(OzProfile.Path) - if err != nil { - fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err) - os.Exit(1) - } - if isInstalled { - fmt.Println("Package divert is \033[0;32minstalled\033[0m for: ", OzProfile.Path) - } else { - fmt.Println("Package divert is \033[0;31mnot installed\033[0m for: ", OzProfile.Path) + checkInstalled := func(cpath string) { + isInstalled, err := isDivertInstalled(cpath) + if err != nil { + fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err) + os.Exit(1) + } + sfmt := "%-37s\033[0m%s\n" + if isInstalled { + fmt.Printf("\033[0;32m"+sfmt, "Package divert is installed for: ", cpath) + } else { + fmt.Printf("\033[0;31m"+sfmt, "Package divert is not installed for: ", cpath) + } } + paths := append([]string{}, OzProfile.Path) + paths = append(paths, OzProfile.Paths...) + for _, pp := range paths { + checkInstalled(pp) + } } func handleCreate(c *cli.Context) { @@ -366,10 +407,15 @@ func loadConfig() *oz.Config { func loadProfile(name, profileDir string) (*oz.Profile, error) { ps, err := oz.LoadProfiles(profileDir) if err != nil { + fmt.Println("DERP1") return nil, err } - return ps.GetProfileByName(name) + p, err := ps.GetProfileByName(name) + if err != nil || p == nil { + return ps.GetProfileByPath(name) + } + return p, nil } diff --git a/network/network.go b/network/network.go index b271492..1c3bdc4 100644 --- a/network/network.go +++ b/network/network.go @@ -18,6 +18,14 @@ const ( ozMaxRandTries = 3 ) +type NetType string + +const( + TYPE_HOST NetType = "host" + TYPE_EMPTY NetType = "empty" + TYPE_BRIDGE NetType = "bridge" +) + type HostNetwork struct { // Gateway ip (bridge ip) Gateway net.IP @@ -35,6 +43,8 @@ type HostNetwork struct { Max uint64 // Bridge interface MAC Address BridgeMAC string + // + Nettype NetType } type SandboxNetwork struct { @@ -48,6 +58,8 @@ type SandboxNetwork struct { Gateway net.IP // IP class (ie: /24) Class string + // + Nettype NetType } var privateNetworkRanges []string diff --git a/network/proxy.go b/network/proxy.go index 6f3c845..77fa55f 100644 --- a/network/proxy.go +++ b/network/proxy.go @@ -10,17 +10,32 @@ import( "sync" "github.com/subgraph/oz/ns" - + "github.com/op/go-logging" ) +type ProxyType string + +const( + PROXY_CLIENT ProxyType = "client" + PROXY_SERVER ProxyType = "server" +) + +type ProtoType string + +const( + PROTO_TCP ProtoType = "tcp" + PROTO_UDP ProtoType = "udp" + PROTO_SOCKET ProtoType = "socket" +) + // Socket list, used to hold ports that should be forwarded type ProxyConfig struct { // One of client, server - Nettype string `json:"type"` + Nettype ProxyType `json:"type"` // One of tcp, udp, socket - Proto string + Proto ProtoType // TCP or UDP port number Port int @@ -40,12 +55,12 @@ var wgProxy sync.WaitGroup func ProxySetup(childPid int, ozSockets []ProxyConfig, log *logging.Logger, ready sync.WaitGroup) error { for _, socket := range ozSockets { - if socket.Nettype == "" || socket.Nettype == "client" { + if socket.Nettype == "" || socket.Nettype == PROXY_CLIENT { err := newProxyClient(childPid, socket.Proto, socket.Destination, socket.Port, log, ready) if err != nil { return fmt.Errorf("Unable to setup client socket forwarding %+v, %s", socket, err) } - } else if socket.Nettype == "server" { + } else if socket.Nettype == PROXY_SERVER { err := newProxyServer(childPid, socket.Proto, socket.Destination, socket.Port, log, ready) if err != nil { return fmt.Errorf("Unable to setup server socket forwarding %+s, %s", socket, err) @@ -59,8 +74,8 @@ func ProxySetup(childPid int, ozSockets []ProxyConfig, log *logging.Logger, read /** * Listener/Client **/ -func proxyClientConn(conn *net.Conn, proto, rAddr string, ready sync.WaitGroup) error { - rConn, err := net.Dial(proto, rAddr) +func proxyClientConn(conn *net.Conn, proto ProtoType, rAddr string, ready sync.WaitGroup) error { + rConn, err := net.Dial(string(proto), rAddr) if err != nil { return fmt.Errorf("Socket: %+v.\n", err) } @@ -71,7 +86,7 @@ func proxyClientConn(conn *net.Conn, proto, rAddr string, ready sync.WaitGroup) return nil } -func newProxyClient(pid int, proto, dest string, port int, log *logging.Logger, ready sync.WaitGroup) error { +func newProxyClient(pid int, proto ProtoType, dest string, port int, log *logging.Logger, ready sync.WaitGroup) error { if dest == "" { dest = "127.0.0.1" } @@ -104,7 +119,7 @@ func newProxyClient(pid int, proto, dest string, port int, log *logging.Logger, return nil } -func proxySocketListener(pid int, proto, lAddr string) (net.Listener, error) { +func proxySocketListener(pid int, proto ProtoType, lAddr string) (net.Listener, error) { fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET) defer ns.Close(fd) if err != nil { @@ -114,7 +129,7 @@ func proxySocketListener(pid int, proto, lAddr string) (net.Listener, error) { return nsSocketListener(fd, proto, lAddr) } -func nsSocketListener(fd uintptr, proto, lAddr string) (net.Listener, error) { +func nsSocketListener(fd uintptr, proto ProtoType, lAddr string) (net.Listener, error) { origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET) defer ns.Close(origNs) defer ns.Set(origNs, ns.CLONE_NEWNET) @@ -124,14 +139,14 @@ func nsSocketListener(fd uintptr, proto, lAddr string) (net.Listener, error) { return nil, err } - return net.Listen(proto, lAddr) + return net.Listen(string(proto), lAddr) } /** * Connect/Server **/ -func proxyServerConn(pid int, conn *net.Conn, proto, rAddr string, log *logging.Logger, ready sync.WaitGroup) (error) { +func proxyServerConn(pid int, conn *net.Conn, proto ProtoType, rAddr string, log *logging.Logger, ready sync.WaitGroup) (error) { rConn, err := socketConnect(pid, proto, rAddr) if err != nil { log.Error("Socket: %+v.", err) @@ -144,7 +159,7 @@ func proxyServerConn(pid int, conn *net.Conn, proto, rAddr string, log *logging. return nil } -func newProxyServer(pid int, proto, dest string, port int, log *logging.Logger, ready sync.WaitGroup) (error) { +func newProxyServer(pid int, proto ProtoType, dest string, port int, log *logging.Logger, ready sync.WaitGroup) (error) { if dest == "" { dest = "127.0.0.1" } @@ -154,7 +169,7 @@ func newProxyServer(pid int, proto, dest string, port int, log *logging.Logger, log.Info("Starting socket server forwarding: %s://%s.", proto, lAddr) - listen, err := net.Listen(proto, lAddr) + listen, err := net.Listen(string(proto), lAddr) if err != nil { return err } @@ -177,7 +192,7 @@ func newProxyServer(pid int, proto, dest string, port int, log *logging.Logger, return nil } -func socketConnect(pid int, proto, rAddr string) (net.Conn, error) { +func socketConnect(pid int, proto ProtoType, rAddr string) (net.Conn, error) { fd, err := ns.OpenProcess(pid, ns.CLONE_NEWNET) defer ns.Close(fd) if err != nil { @@ -187,7 +202,7 @@ func socketConnect(pid int, proto, rAddr string) (net.Conn, error) { return nsProxySocketConnect(fd, proto, rAddr) } -func nsProxySocketConnect(fd uintptr, proto, rAddr string) (net.Conn, error) { +func nsProxySocketConnect(fd uintptr, proto ProtoType, rAddr string) (net.Conn, error) { origNs, _ := ns.OpenProcess(os.Getpid(), ns.CLONE_NEWNET) defer ns.Close(origNs) defer ns.Set(origNs, ns.CLONE_NEWNET) @@ -197,6 +212,6 @@ func nsProxySocketConnect(fd uintptr, proto, rAddr string) (net.Conn, error) { return nil, err } - return net.Dial(proto, rAddr) + return net.Dial(string(proto), rAddr) } diff --git a/oz-daemon/client.go b/oz-daemon/client.go index b0ed62c..4b75276 100644 --- a/oz-daemon/client.go +++ b/oz-daemon/client.go @@ -53,7 +53,7 @@ func ListSandboxes() ([]SandboxInfo, error) { return body.Sandboxes, nil } -func Launch(arg string, args, env []string, noexec bool) error { +func Launch(arg, cpath string, args, env []string, noexec bool) error { idx, name, err := parseProfileArg(arg) if err != nil { return err @@ -63,6 +63,7 @@ func Launch(arg string, args, env []string, noexec bool) error { resp, err := clientSend(&LaunchMsg{ Index: idx, Name: name, + Path: cpath, Pwd: pwd, Args: args, Env: env, diff --git a/oz-daemon/daemon.go b/oz-daemon/daemon.go index 708281d..ea290b0 100644 --- a/oz-daemon/daemon.go +++ b/oz-daemon/daemon.go @@ -71,7 +71,7 @@ func initialize() *daemonState { d.nextDisplay = 100 for _, pp := range d.profiles { - if pp.Networking.Nettype == "bridge" { + if pp.Networking.Nettype == network.TYPE_BRIDGE { d.log.Info("Initializing bridge networking") htn, err := network.BridgeInit(d.config.BridgeMACAddr, d.config.NMIgnoreFile, d.log) if err != nil { @@ -127,10 +127,11 @@ func (d *daemonState) handleListProfiles(msg *ListProfilesMsg, m *ipc.Message) e func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { d.Debug("Launch message received: %+v", msg) - p, err := d.getProfileByIdxOrName(msg.Index, msg.Name) + p, err := d.getProfileFromLaunchMsg(msg) if err != nil { return m.Respond(&ErrorMsg{err.Error()}) } + if sbox := d.getRunningSandboxByName(p.Name); sbox != nil { if msg.Noexec { errmsg := "Asked to launch program but sandbox is running and noexec is set!" @@ -138,12 +139,12 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { return m.Respond(&ErrorMsg{errmsg}) } else { d.Info("Found running sandbox for `%s`, running program there", p.Name) - sbox.launchProgram(msg.Pwd, msg.Args, d.log) + sbox.launchProgram(msg.Path, msg.Pwd, msg.Args, d.log) } } else { d.Debug("Would launch %s", p.Name) - env := d.sanitizeEnvironment(p, msg.Env) - _, err = d.launch(p, msg.Pwd, msg.Args, env, msg.Noexec, m.Ucred.Uid, m.Ucred.Gid, d.log) + msg.Env = d.sanitizeEnvironment(p, msg.Env) + _, err = d.launch(p, msg, m.Ucred.Uid, m.Ucred.Gid, d.log) if err != nil { d.Warning("Launch of %s failed: %v", p.Name, err) return m.Respond(&ErrorMsg{err.Error()}) @@ -154,7 +155,7 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { func (d *daemonState) sanitizeEnvironment(p *oz.Profile, oldEnv []string) ([]string) { newEnv := []string{} - + for _, EnvItem := range d.config.EnvironmentVars { for _, OldItem := range oldEnv { if strings.HasPrefix(OldItem, EnvItem+"=") { @@ -183,7 +184,7 @@ func (d *daemonState) sanitizeEnvironment(p *oz.Profile, oldEnv []string) ([]str } } } - + return newEnv } @@ -207,6 +208,28 @@ func (d *daemonState) sandboxById(id int) *Sandbox { return nil } +func (d *daemonState) getProfileFromLaunchMsg(msg *LaunchMsg) (*oz.Profile, error) { + if msg.Index == 0 && msg.Name == "" { + return d.getProfileByPath(msg.Path) + } + return d.getProfileByIdxOrName(msg.Index, msg.Name) +} + +func (d *daemonState) getProfileByPath(cpath string) (*oz.Profile, error) { + for _, p := range d.profiles { + if p.Path == cpath { + return p, nil + } + for _, pp := range p.Paths { + if pp == cpath { + return p, nil + } + } + } + + return nil, fmt.Errorf("could not find profile path '%s'", cpath) +} + func (d *daemonState) getProfileByIdxOrName(index int, name string) (*oz.Profile, error) { if len(name) == 0 { if index < 1 || index > len(d.profiles) { @@ -229,7 +252,7 @@ func (d *daemonState) getRunningSandboxByName(name string) *Sandbox { return sb } } - + return nil } diff --git a/oz-daemon/launch.go b/oz-daemon/launch.go index 44cbbf3..d181b94 100644 --- a/oz-daemon/launch.go +++ b/oz-daemon/launch.go @@ -36,7 +36,7 @@ type Sandbox struct { network *network.SandboxNetwork } -func createInitCommand(initPath, name, chroot string, env []string, uid uint32, display int, stn *network.SandboxNetwork, nettype string) *exec.Cmd { +func createInitCommand(initPath, name, chroot string, env []string, uid uint32, display int, stn *network.SandboxNetwork) *exec.Cmd { cmd := exec.Command(initPath) cmd.Dir = "/" @@ -45,7 +45,7 @@ func createInitCommand(initPath, name, chroot string, env []string, uid uint32, cloneFlags |= syscall.CLONE_NEWPID cloneFlags |= syscall.CLONE_NEWUTS - if nettype != "host" { + if stn.Nettype != network.TYPE_HOST { cloneFlags |= syscall.CLONE_NEWNET } @@ -74,7 +74,7 @@ func createInitCommand(initPath, name, chroot string, env []string, uid uint32, return cmd } -func (d *daemonState) launch(p *oz.Profile, pwd string, args, env []string, noexec bool, uid, gid uint32, log *logging.Logger) (*Sandbox, error) { +func (d *daemonState) launch(p *oz.Profile, msg *LaunchMsg, uid, gid uint32, log *logging.Logger) (*Sandbox, error) { u, err := user.LookupId(fmt.Sprintf("%d", uid)) if err != nil { return nil, fmt.Errorf("failed to lookup user for uid=%d: %v", uid, err) @@ -84,20 +84,21 @@ func (d *daemonState) launch(p *oz.Profile, pwd string, args, env []string, noex return nil, err } display := 0 - if p.XServer.Enabled && p.Networking.Nettype == "host" { + if p.XServer.Enabled && p.Networking.Nettype == network.TYPE_HOST { display = d.nextDisplay d.nextDisplay += 1 } stn := new(network.SandboxNetwork) - if p.Networking.Nettype == "bridge" { + stn.Nettype = p.Networking.Nettype + if p.Networking.Nettype == network.TYPE_BRIDGE { stn, err = network.PrepareSandboxNetwork(d.network, log) if err != nil { return nil, fmt.Errorf("Unable to prepare veth network: %+v", err) } } - cmd := createInitCommand(d.config.InitPath, p.Name, fs.Root(), env, uid, display, stn, p.Networking.Nettype) + cmd := createInitCommand(d.config.InitPath, p.Name, fs.Root(), msg.Env, uid, display, stn) log.Debug("Command environment: %+v", cmd.Env) pp, err := cmd.StderrPipe() if err != nil { @@ -124,18 +125,18 @@ func (d *daemonState) launch(p *oz.Profile, pwd string, args, env []string, noex network: stn, } - if p.Networking.Nettype == "bridge" { + if p.Networking.Nettype == network.TYPE_BRIDGE { if err := network.NetInit(stn, d.network, cmd.Process.Pid, log); err != nil { cmd.Process.Kill() fs.Cleanup() return nil, fmt.Errorf("Unable to create veth networking: %+v", err) } } - + sbox.ready.Add(1) go sbox.logMessages() - - if p.Networking.Nettype != "host" && len(p.Networking.Sockets) > 0 { + + if p.Networking.Nettype != network.TYPE_HOST && len(p.Networking.Sockets) > 0 { go func() { sbox.ready.Wait() err := network.ProxySetup(sbox.init.Process.Pid, p.Networking.Sockets, d.log, sbox.ready) @@ -145,10 +146,10 @@ func (d *daemonState) launch(p *oz.Profile, pwd string, args, env []string, noex }() } - if !noexec { + if !msg.Noexec { go func () { sbox.ready.Wait() - go sbox.launchProgram(pwd, args, log) + go sbox.launchProgram(msg.Path, msg.Pwd, msg.Args, log) }() } @@ -158,13 +159,13 @@ func (d *daemonState) launch(p *oz.Profile, pwd string, args, env []string, noex go sbox.startXpraClient() }() } - + d.nextSboxId += 1 d.sandboxes = append(d.sandboxes, sbox) return sbox, nil } -func (sbox *Sandbox) launchProgram(pwd string, args []string, log *logging.Logger) { +func (sbox *Sandbox) launchProgram(cpath, pwd string, args []string, log *logging.Logger) { if sbox.profile.AllowFiles { for _, fpath := range args { if _, err := os.Stat(fpath); err == nil { @@ -178,8 +179,8 @@ func (sbox *Sandbox) launchProgram(pwd string, args []string, log *logging.Logge } } } - - err := ozinit.RunProgram(sbox.addr, pwd, args) + + err := ozinit.RunProgram(sbox.addr, cpath, pwd, args) if err != nil { log.Error("start shell command failed: %v", err) } @@ -190,7 +191,7 @@ func (sbox *Sandbox) remove(log *logging.Logger) { for _, sb := range sbox.daemon.sandboxes { if sb == sbox { sb.fs.Cleanup() - if sb.profile.Networking.Nettype == "bridge" { + if sb.profile.Networking.Nettype == network.TYPE_BRIDGE { sb.network.Cleanup(log) } } else { diff --git a/oz-daemon/protocol.go b/oz-daemon/protocol.go index 0028642..b713128 100644 --- a/oz-daemon/protocol.go +++ b/oz-daemon/protocol.go @@ -32,6 +32,7 @@ type ListProfilesResp struct { type LaunchMsg struct { Index int "Launch" + Path string Name string Pwd string Args []string diff --git a/oz-init/client.go b/oz-init/client.go index 2a6e257..12ec4c7 100644 --- a/oz-init/client.go +++ b/oz-init/client.go @@ -41,12 +41,12 @@ func Ping(addr string) error { } } -func RunProgram(addr, pwd string, args []string) error { +func RunProgram(addr, cpath, pwd string, args []string) error { c, err := clientConnect(addr) if err != nil { return err } - rr, err := c.ExchangeMsg(&RunProgramMsg{Args: args, Pwd: pwd}) + rr, err := c.ExchangeMsg(&RunProgramMsg{Path: cpath, Args: args, Pwd: pwd}) resp := <-rr.Chan() rr.Done() c.Close() diff --git a/oz-init/init.go b/oz-init/init.go index 4d8fd98..45258da 100644 --- a/oz-init/init.go +++ b/oz-init/init.go @@ -4,7 +4,6 @@ import ( "bufio" "fmt" "io" - "io/ioutil" "net" "os" "os/exec" @@ -69,8 +68,9 @@ func parseArgs() *initState { log.Error("oz-init must run as root\n") os.Exit(1) } - pcontents, _ := ioutil.ReadFile("/proc/1/cmdline") - if len(pcontents) > 0 { + // We should check that the file contains config.InitPath, but + // since proc is not mounted in this state is still doesn't exist. + if _, err := os.Stat("/proc/1/cmdline"); !os.IsNotExist(err) { log.Error("What are you doing? Oz-init cannot be launched manually") os.Exit(1) } @@ -156,13 +156,13 @@ func parseArgs() *initState { env = append(env, e) } } - + env = append(env, "PATH=/usr/bin:/bin") - + if p.XServer.Enabled { env = append(env, "DISPLAY=:"+strconv.Itoa(display)) } - + return &initState{ log: log, config: config, @@ -186,8 +186,8 @@ func (st *initState) runInit() { if homedir, _ := st.fs.GetHomeDir(); homedir != "" { st.launchEnv = append(st.launchEnv, "HOME="+homedir) } - - if st.profile.Networking.Nettype != "host" { + + if st.profile.Networking.Nettype != network.TYPE_HOST { err := network.NetSetup(st.network) if err != nil { st.log.Error("Unable to setup networking: %+v", err) @@ -289,12 +289,15 @@ func (st *initState) readXpraOutput(r io.ReadCloser) { } } -func (st *initState) launchApplication(pwd string, cmdArgs []string) (*exec.Cmd, error) { +func (st *initState) launchApplication(cpath, pwd string, cmdArgs []string) (*exec.Cmd, error) { suffix := "" if st.config.DivertSuffix != "" { suffix = "."+st.config.DivertSuffix } - cmd := exec.Command(st.profile.Path+suffix) + if cpath == "" { + cpath = st.profile.Path + } + cmd := exec.Command(cpath+suffix) stdout, err := cmd.StdoutPipe() if err != nil { st.log.Warning("Failed to create stdout pipe: %v", err) @@ -311,13 +314,13 @@ func (st *initState) launchApplication(pwd string, cmdArgs []string) (*exec.Cmd, Gid: uint32(st.gid), } cmd.Env = append(cmd.Env, st.launchEnv...) - + cmd.Args = append(cmd.Args, cmdArgs...) - + if _, err := os.Stat(pwd); err == nil { cmd.Dir = pwd } - + if err := cmd.Start(); err != nil { st.log.Warning("Failed to start application (%s): %v", st.profile.Path, err) return nil, err @@ -326,7 +329,7 @@ func (st *initState) launchApplication(pwd string, cmdArgs []string) (*exec.Cmd, go st.readApplicationOutput(stdout, "stdout") go st.readApplicationOutput(stderr, "stderr") - + return cmd, nil } @@ -357,7 +360,7 @@ func handlePing(ping *PingMsg, msg *ipc.Message) error { func (st *initState) handleRunProgram(rp *RunProgramMsg, msg *ipc.Message) error { st.log.Info("Run program message received: %+v", rp) - _, err := st.launchApplication(rp.Pwd, rp.Args) + _, err := st.launchApplication(rp.Path, rp.Pwd, rp.Args) if err != nil { err := msg.Respond(&ErrorMsg{Msg: err.Error()}) return err diff --git a/oz-init/protocol.go b/oz-init/protocol.go index 969c20d..9dd5f2f 100644 --- a/oz-init/protocol.go +++ b/oz-init/protocol.go @@ -21,6 +21,7 @@ type RunShellMsg struct { type RunProgramMsg struct { Args []string "RunProgram" Pwd string + Path string } var messageFactory = ipc.NewMsgFactory( diff --git a/oz/main.go b/oz/main.go index 913b14b..88f0c5c 100644 --- a/oz/main.go +++ b/oz/main.go @@ -5,6 +5,7 @@ import ( "io" "os" "path" + "path/filepath" "strconv" "github.com/subgraph/oz" @@ -41,7 +42,14 @@ func runSandbox() { os.Exit(1) } - err := daemon.Launch(runBasename, os.Args[1:], os.Environ(), false) + name := "0" + cpath := os.Args[0] + if !filepath.IsAbs(os.Args[0]) { + // TODO: Check for executable in path... + name = cpath + cpath = "" + } + err := daemon.Launch(name, cpath, os.Args[1:], os.Environ(), false) if err != nil { fmt.Fprintf(os.Stderr, "launch command failed: %v.\n", err) os.Exit(1) @@ -121,7 +129,7 @@ func handleLaunch(c *cli.Context) { fmt.Println("Argument needed to launch command") os.Exit(1) } - err := daemon.Launch(c.Args()[0], c.Args()[1:], os.Environ(), noexec) + err := daemon.Launch(c.Args()[0], "", c.Args()[1:], os.Environ(), noexec) if err != nil { fmt.Printf("launch command failed: %v\n", err) os.Exit(1) diff --git a/profile.go b/profile.go index 738cb19..2ab5a91 100644 --- a/profile.go +++ b/profile.go @@ -5,7 +5,7 @@ import ( "fmt" "io/ioutil" "path" - + "github.com/subgraph/oz/network" ) @@ -14,6 +14,8 @@ type Profile struct { Name string // Path to binary to launch Path string + // List of path to binaries matching this sandbox + Paths []string // Path of the config file ProfilePath string `json:"-"` // Optional path of binary to watch for watchdog purposes if different than Path @@ -70,7 +72,7 @@ type EnvVar struct { // Sandbox network definition type NetworkProfile struct { // One of empty, host, bridge - Nettype string `json:"type"` + Nettype network.NetType `json:"type"` // Name of the bridge to attach to //Bridge string @@ -86,6 +88,20 @@ var loadedProfiles []*Profile type Profiles []*Profile +func NewDefaultProfile() *Profile { + return &Profile{ + Multi: false, + AllowFiles: false, + XServer: XServerConf{ + Enabled: true, + EnableTray: false, + UseDBUS: false, + UsePulseAudio: false, + DisableAudio: true, + }, + } +} + func (ps Profiles) GetProfileByName(name string) (*Profile, error) { if loadedProfiles == nil { ps, err := LoadProfiles(defaultProfileDirectory) @@ -116,6 +132,11 @@ func (ps Profiles) GetProfileByPath(bpath string) (*Profile, error) { if p.Path == bpath { return p, nil } + for _, pp := range p.Paths { + if pp == bpath { + return p, nil + } + } } return nil, nil } @@ -136,7 +157,7 @@ func LoadProfiles(dir string) (Profiles, error) { ps = append(ps, p) } } - + loadedProfiles = ps return ps, nil } diff --git a/profiles/gajim.json b/profiles/gajim.json index 57914da..8fd36b6 100644 --- a/profiles/gajim.json +++ b/profiles/gajim.json @@ -1,5 +1,10 @@ { -"path": "/usr/bin/gajim" +"name": "gajim" +, "path": "/usr/bin/gajim" +, "paths": [ + "/usr/bin/gajim-history-manager" + , "/usr/bin/gajim-remote" +] , "xserver": { "enabled": true , "enable_tray": true diff --git a/profiles/libreoffice.json b/profiles/libreoffice.json new file mode 100644 index 0000000..cb5cec6 --- /dev/null +++ b/profiles/libreoffice.json @@ -0,0 +1,31 @@ +{ +"name": "libreoffice" +, "path": "/usr/bin/libreoffice" +, "paths": [ + "/usr/bin/lowriter" + , "/usr/bin/lobase" + , "/usr/bin/localc" + , "/usr/share/libreoffice/bin/lo-xlate-lang" + , "/usr/bin/loffice" + , "/usr/bin/unopkg" + , "/usr/bin/lofromtemplate" + , "/usr/bin/soffice" + , "/usr/bin/lodraw" + , "/usr/bin/loimpress" + , "/usr/bin/lomath" + , "/usr/bin/loweb" + , "/usr/bin/lowriter" +] +, "allow_files": true +, "xserver": { + "enabled": true + , "enable_tray": true + , "tray_icon":"/usr/share/icons/gnome/scalable/apps/libreoffice-startcenter.svg" + , "window_icon":"/usr/share/icons/gnome/scalable/apps/libreoffice-startcenter.svg" + , "disable_audio": true + , "use_pulse_audio": false +} +, "networking":{ + "type":"empty" +} +}