From f7fcf24f71c324b65c91ce759fcd1650f93c1759 Mon Sep 17 00:00:00 2001 From: xSmurf Date: Sun, 7 Jun 2015 23:18:21 +0000 Subject: [PATCH] Working networking and minor fixes (mainly config flags) --- config.go | 14 ++- fs/item.go | 2 +- fs/setup.go | 22 +++- network/daemon.go | 245 ++++++++++++++++++++++++++++++------------ network/network.go | 102 +++++++++++++++--- network/ozinit.go | 157 +++++++++------------------ oz-daemon/daemon.go | 34 +++--- oz-daemon/launch.go | 62 ++++++++--- oz-daemon/protocol.go | 5 - oz-init/init.go | 58 ++++++++-- 10 files changed, 458 insertions(+), 243 deletions(-) diff --git a/config.go b/config.go index a13fe2e..3f11029 100644 --- a/config.go +++ b/config.go @@ -6,11 +6,13 @@ import ( ) type Config struct { - ProfileDir string `json:"profile_dir"` - ShellPath string `json:"shell_path"` - SandboxPath string `json:"sandbox_path"` - AllowRootShell bool `json:"allow_root_shell"` - LogXpra bool `json:"log_xpra"` + ProfileDir string `json:"profile_dir"` + ShellPath string `json:"shell_path"` + SandboxPath string `json:"sandbox_path"` + BridgeMACAddr string `json:"bridge_mac"` + NMIgnoreFile string `json:"nm_ignore_file"` + AllowRootShell bool `json:"allow_root_shell"` + LogXpra bool `json:"log_xpra"` } const DefaultConfigPath = "/etc/oz/oz.conf" @@ -20,6 +22,8 @@ func NewDefaultConfig() *Config { ProfileDir: "/var/lib/oz/cells.d", ShellPath: "/bin/bash", SandboxPath: "/srv/oz", + NMIgnoreFile: "/etc/NetworkManager/conf.d/oz.conf", + BridgeMACAddr: "6A:A8:2E:56:E8:9C", AllowRootShell: false, LogXpra: false, } diff --git a/fs/item.go b/fs/item.go index 20fdc08..1b42967 100644 --- a/fs/item.go +++ b/fs/item.go @@ -116,7 +116,7 @@ func (mi *mountItem) bindItem() error { target := mi.targetPath() _, err = os.Stat(target) if err == nil || !os.IsNotExist(err) { - mi.fs.log.Warning("Target (%s) already exists, ignoring", target) + mi.fs.log.Warning("Target (%s > %s) already exists, ignoring", src, target) return nil } if sinfo.IsDir() { diff --git a/fs/setup.go b/fs/setup.go index 795f8f3..af0cfd4 100644 --- a/fs/setup.go +++ b/fs/setup.go @@ -11,16 +11,16 @@ import ( ) var basicBindDirs = []string{ - "/bin", "/lib", "/lib64", "/usr", "/etc", "/var/lib/oz", + "/bin", "/lib", "/lib64", "/usr", "/etc", } var basicEmptyDirs = []string{ "/sbin", "/var", "/var/lib", "/var/cache", "/home", "/boot", "/tmp", "/run", "/run/user", - "/run/lock", "/root", "/opt", - "/srv", "/dev", "/proc", "/sys", - "/mnt", "/media", + "/run/shm", "/run/lock", "/root", + "/opt", "/srv", "/dev", "/proc", + "/sys", "/mnt", "/media", } var basicBlacklist = []string{ @@ -39,7 +39,19 @@ var basicSymlinks = [][2]string{ {"/run/lock", "/var/lock"}, } -func (fs *Filesystem) Setup() error { +func (fs *Filesystem) Setup(profilesPath string) error { + profilePathInBindDirs := false + for _, bd := range basicBindDirs { + if bd == profilesPath { + profilePathInBindDirs = true + break; + } + } + + if profilePathInBindDirs == false { + basicBindDirs = append(basicBindDirs, profilesPath) + } + if fs.xpra != "" { if err := fs.createXpraDir(); err != nil { return err diff --git a/network/daemon.go b/network/daemon.go index e3864fc..b00620a 100644 --- a/network/daemon.go +++ b/network/daemon.go @@ -4,122 +4,232 @@ import( //Builtin "errors" "fmt" - //"math" + "math/rand" "net" "os" - //"os/exec" "strings" + "time" //Internal + "github.com/op/go-logging" //External "github.com/milosgajdos83/tenus" - "github.com/op/go-logging" ) - -func BridgeInit(log *logging.Logger) (*HostNetwork, error) { - htn := new(HostNetwork) - +func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*HostNetwork, error) { if os.Getpid() == 1 { panic(errors.New("Cannot use netinit from child.")) } - - // Fetch the bridge interface by ifname - brL, err := net.InterfaceByName(ozDefaultInterfaceBridge) + + htn := &HostNetwork{ + BridgeMAC: bridgeMAC, + } + + if _, err := os.Stat(nmIgnoreFile); os.IsNotExist(err) { + log.Warning("Warning! Network Manager may not properly configured to ignore the bridge interface! This may result in management conflicts!") + } + + br, err := tenus.BridgeFromName(ozDefaultInterfaceBridge) if err != nil { log.Info("Bridge not found, attempting to create a new one") - - _, err = createNewBridge(log) + + br, err = tenus.NewBridgeWithName(ozDefaultInterfaceBridge) if err != nil { return nil, fmt.Errorf("Unable to create bridge %+v", err) } - - // Load the new interface - brL, _ = net.InterfaceByName(ozDefaultInterfaceBridge) - } else { - log.Info("Bridge already exists attempting to reuse it") } - - // Lookup the bridge ip addresses - addrs, _ := brL.Addrs() - if len(addrs) == 0 { - return nil, errors.New("Host bridge does not have an IP address assigned") + + if err:= htn.configureBridgeInterface(br, log); err != nil { + return nil, err + } + + brL := br.NetInterface() + addrs, err := brL.Addrs() + if err != nil { + return nil, fmt.Errorf("Unable to get bridgre interface addresses: %+v", err) + } + + // Build the ip range which we will use for the network + if err := htn.buildBridgeNetwork(addrs); err != nil { + return nil, err } - // Try to build the network config from the bridge's address - addrIndex := -1 - for i, addr := range addrs { - bIP, brIP, _ := net.ParseCIDR(addr.String()) - - // Discard IPv6 (TODO...) - if bIP.To4() != nil { - addrIndex = i + return htn, nil - bMask := []byte(brIP.Mask) +} - htn.netmask = net.IPv4(bMask[0], bMask[1], bMask[2], bMask[3]) - htn.gateway = net.ParseIP(strings.Split(addr.String(), "/")[0]) - htn.class = strings.Split(addr.String(), "/")[1] - htn.broadcast = net_getbroadcast(bIP, brIP.Mask) +func PrepareSandboxNetwork(htn *HostNetwork, log *logging.Logger) (*SandboxNetwork, error) { + stn := new(SandboxNetwork) + + stn.VethHost = tenus.MakeNetInterfaceName(ozDefaultInterfacePrefix) + stn.VethGuest = stn.VethHost + "1" + + stn.Gateway = htn.Gateway + stn.Class = htn.Class + + // Allocate a new IP address + stn.Ip = getFreshIP(htn.Min, htn.Max, log) + if stn.Ip == "" { + return nil, errors.New("Unable to acquire random IP") + } + + return stn, nil +} - htn.min = inet_aton(bIP) - htn.min++ +func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.Logger) error { + if os.Getpid() == 1 { + panic(errors.New("Cannot use netSetup from child.")) + } + + // Seed random number generator (poorly but we're not doing crypto) + rand.Seed(time.Now().Unix() ^ int64((os.Getpid() + childPid))) - htn.max = inet_aton(htn.broadcast) - htn.max-- + log.Info("Configuring host veth pair '%s' with: %s", stn.VethHost, stn.Ip + "/" + htn.Class) - break - } + // Fetch the bridge from the ifname + br, err := tenus.BridgeFromName(ozDefaultInterfaceBridge) + if err != nil { + return fmt.Errorf("Unable to attach to bridge interface %, %s.", ozDefaultInterfaceBridge, err) + } + // Make sure the bridge is configured and the link is up + // This really shouldn't be needed, but Network-Manager is a PITA + // and even if you actualy ignore the interface there's a race + // between the interface being created and setting it's hwaddr + //if err := htn.configureBridgeInterface(br, log); err != nil { + // return fmt.Errorf("Unable to reconfigure bridge: %+v", err) + //} + + // Create the veth pair + veth, err := tenus.NewVethPairWithOptions(stn.VethHost, tenus.VethOptions{PeerName: stn.VethGuest}) + if err != nil { + return fmt.Errorf("Unable to create veth pair %s, %s.", stn.VethHost, err) } - if addrIndex < 0 { - return nil, errors.New("Could not find IPv4 for bridge interface") + // Fetch the newly created hostside veth + vethIf, err := net.InterfaceByName(stn.VethHost) + if err != nil { + return fmt.Errorf("Unable to fetch veth pair %s, %s.", stn.VethHost, err) } - return htn, nil -} + // Add the host side veth to the bridge + if err := br.AddSlaveIfc(vethIf); err != nil { + return fmt.Errorf("Unable to add veth pair %s to bridge, %s.", stn.VethHost, err) + } + // Bring the host side veth interface up + if err := veth.SetLinkUp(); err != nil { + return fmt.Errorf("Unable to bring veth pair %s up, %s.", stn.VethHost, err) + } -// Create a new bridge on the host -// Assign it an unused range -// And bring the interface up -func createNewBridge(log *logging.Logger) (tenus.Bridger, error) { - if os.Getpid() == 1 { - panic(errors.New("Cannot use netinit from child.")) + // Assign the veth path to the namespace + pid := childPid + if err := veth.SetPeerLinkNsPid(pid); err != nil { + return fmt.Errorf("Unable to add veth pair %s to namespace, %s.", stn.VethHost, err) } - // Create the bridge - br, err := tenus.NewBridgeWithName(ozDefaultInterfaceBridge) + // Parse the ip/class into the the appropriate formats + vethGuestIp, vethGuestIpNet, err := net.ParseCIDR(stn.Ip + "/" + htn.Class) if err != nil { - return nil, err + return fmt.Errorf("Unable to parse ip %s, %s.", stn.Ip, err) + } + + // Set interface address in the namespace + if err := veth.SetPeerLinkNetInNs(pid, vethGuestIp, vethGuestIpNet, nil); err != nil { + return fmt.Errorf("Unable to parse ip link in namespace, %s.", err) } - // Lookup an empty ip range - brIp, brIpNet, err := findEmptyRange(log) - if err != nil { - return nil, errors.New("Could not find an ip range to assign to the bridge") + return nil + +} + +func (stn *SandboxNetwork) Cleanup(log *logging.Logger) { + if os.Getpid() == 1 { + panic(errors.New("Cannot use Cleanup from child.")) } + + if _, err := net.InterfaceByName(stn.VethHost); err != nil { + log.Info("No veth found to cleanup") + return + } + + tenus.DeleteLink(stn.VethHost) +} - // Setup the bridge's address - if err := br.SetLinkIp(brIp, brIpNet); err != nil { - return nil, err +func (htn *HostNetwork) configureBridgeInterface(br tenus.Bridger, log *logging.Logger) error { + // Set the bridge mac address so it can be fucking ignored by Network-Manager. + if htn.BridgeMAC != "" { + if err := br.SetLinkMacAddress(htn.BridgeMAC); err != nil { + return fmt.Errorf("Unable to set MAC address for gateway", err) + } + } + + if htn.Gateway == nil { + // Lookup an empty ip range + brIp, brIpNet, err := findEmptyRange() + if err != nil { + return fmt.Errorf("Could not find an ip range to assign to the bridge") + } + htn.Gateway = brIp + htn.GatewayNet = brIpNet + log.Info("Found available range: %+v", htn.GatewayNet.String()) + } + + if err := br.SetLinkIp(htn.Gateway, htn.GatewayNet); err != nil { + if os.IsExist(err) { + log.Info("Bridge IP appears to be already assigned") + } else { + return fmt.Errorf("Unable to set gateway IP", err) + } } // Bridge the interface up - if err = br.SetLinkUp(); err != nil { - return nil, fmt.Errorf("Unable to bring bridge '%+v' up: %+v", ozDefaultInterfaceBridge, err) + if err := br.SetLinkUp(); err != nil { + return fmt.Errorf("Unable to bring bridge '%+v' up: %+v", ozDefaultInterfaceBridge, err) } - return br, nil - + return nil } +func (htn *HostNetwork)buildBridgeNetwork(addrs []net.Addr) error { + // Try to build the network config from the bridge's address + addrIndex := -1 + for i, addr := range addrs { + bIP, brIP, _ := net.ParseCIDR(addr.String()) + + // Discard IPv6 (TODO...) + if bIP.To4() != nil { + addrIndex = i + + bMask := []byte(brIP.Mask) + + htn.Netmask = net.IPv4(bMask[0], bMask[1], bMask[2], bMask[3]) + htn.Gateway = net.ParseIP(strings.Split(addr.String(), "/")[0]) + htn.Class = strings.Split(addr.String(), "/")[1] + htn.Broadcast = net_getbroadcast(bIP, brIP.Mask) + + htn.Min = inet_aton(bIP) + htn.Min++ + + htn.Max = inet_aton(htn.Broadcast) + htn.Max-- + + break + } + + } + + if addrIndex < 0 { + return errors.New("Could not find IPv4 for bridge interface") + } + + return nil +} // Look at all the assigned IP address and try to find an available range // Returns a ip range in the CIDR form if found or an empty string -func findEmptyRange(log *logging.Logger) (net.IP, *net.IPNet, error) { +func findEmptyRange() (net.IP, *net.IPNet, error) { type localNet struct { min uint64 max uint64 @@ -186,10 +296,9 @@ func findEmptyRange(log *logging.Logger) (net.IP, *net.IPNet, error) { bMin = bMax - 0xFE } + // XXX availableRange = inet_ntoa(bMin).String() + "/24" - log.Info("Found available range: %+v", availableRange) - return net.ParseCIDR(availableRange) } diff --git a/network/network.go b/network/network.go index dc56b6f..b5f9fe8 100644 --- a/network/network.go +++ b/network/network.go @@ -2,9 +2,12 @@ package network import ( //Builtin + "fmt" "net" "strings" "strconv" + + "github.com/op/go-logging" ) const ( @@ -16,32 +19,35 @@ const ( ) type HostNetwork struct { - // Host bridge IP address - hostip net.IP // Gateway ip (bridge ip) - gateway net.IP + Gateway net.IP + // Gateway ip (bridge ip) + GatewayNet *net.IPNet // Bridge netmask - netmask net.IP + Netmask net.IP // Broadcast ip - broadcast net.IP + Broadcast net.IP // IP class (ie: /24) - class string + Class string // Minimum longip available ip - min uint64 + Min uint64 // Maximum longip available ip - max uint64 - + Max uint64 + // Bridge interface MAC Address + BridgeMAC string } type SandboxNetwork struct { // Name of the veth in the host - vethHost string + VethHost string // Temporary name of the guest' veth in the host - vethGuest string + VethGuest string // Guest ip address - ip string - - host *HostNetwork + Ip string + // Gateway ip (bridge ip) + Gateway net.IP + // IP class (ie: /24) + Class string } var privateNetworkRanges []string @@ -66,6 +72,74 @@ func init() { } +// Print status of the network interfaces +func NetPrint(log *logging.Logger) { + strLine := "" + ifs, _ := net.Interfaces() + + strHeader := fmt.Sprintf("%-15.15s%-30.30s%-16.16s%-6.6s", "Interface", "IP", "Mask", "Status") + strHr := "" + ii := len(strHeader) + for i := 0; i < ii; i++ { + strHr += "-" + } + + log.Info(strHr) + + log.Info(strHeader) + + for _, netif := range ifs { + addrs, _ := netif.Addrs() + + strLine = fmt.Sprintf("%-15.14s", netif.Name) + + if len(addrs) > 0 { + strLine += fmt.Sprintf("%-30.30s", addrs[0]) + + bIP, brIP, _ := net.ParseCIDR(addrs[0].String()) + if bIP.To4() != nil { + bMask := []byte(brIP.Mask) + strLine += fmt.Sprintf("%-16.16s", net.IPv4(bMask[0], bMask[1], bMask[2], bMask[3]).String()) + } else { + strLine += fmt.Sprintf("%-16.16s", "") + } + } else { + strLine += fmt.Sprintf("%-30.30s%-16.16s", "", "") + } + + if netif.Flags&net.FlagUp == 1 { + strLine += fmt.Sprintf("%-6.6s", "up") + } else { + strLine += fmt.Sprintf("%-6.6s", "down") + } + + if len(addrs) > 1 { + strLine += fmt.Sprintf("") + + for _, addr := range addrs[1:] { + strLine += fmt.Sprintf("%-15.15s%-30.30s", "", addr) + + bIP, brIP, _ := net.ParseCIDR(addr.String()) + + if bIP.To4() != nil { + bMask := []byte(brIP.Mask) + strLine += fmt.Sprintf("%-20.20s", net.IPv4(bMask[0], bMask[1], bMask[2], bMask[3]).String()) + } else { + strLine += fmt.Sprintf("%-16.16s", "") + } + } + } + + strLine += fmt.Sprintf("\n") + + log.Info(strLine) + } + + log.Info(strHr) + +} + + // Convert longip to net.IP func inet_ntoa(ipnr uint64) net.IP { var bytes [4]byte diff --git a/network/ozinit.go b/network/ozinit.go index 79e0284..1ffe184 100644 --- a/network/ozinit.go +++ b/network/ozinit.go @@ -4,145 +4,90 @@ import ( //Builtin "errors" "fmt" - //"math" "math/rand" - "net" "os" "time" + // Internal + "github.com/op/go-logging" + //External "github.com/j-keck/arping" "github.com/milosgajdos83/tenus" - "github.com/op/go-logging" ) -func NetInit(log *logging.Logger, htn *HostNetwork, childPid int) (*SandboxNetwork, error) { - if os.Getpid() == 1 { - panic(errors.New("Cannot use netSetup from child.")) - } - - stn := new(SandboxNetwork) - - stn.vethHost = tenus.MakeNetInterfaceName(ozDefaultInterfacePrefix) - stn.vethGuest = stn.vethHost + "1" - - // Seed random number generator (poorly but we're not doing crypto) - rand.Seed(time.Now().Unix() ^ int64((os.Getpid() + childPid))) - - log.Info("Configuring host veth pair: %s", stn.vethHost) - - // Fetch the bridge from the ifname - br, err := tenus.BridgeFromName(ozDefaultInterfaceBridge) - if err != nil { - return nil, fmt.Errorf("Unable to attach to bridge interface %, %s.", ozDefaultInterfaceBridge, err) - } - - // Create the veth pair - veth, err := tenus.NewVethPairWithOptions(stn.vethHost, tenus.VethOptions{PeerName: stn.vethGuest}) - if err != nil { - return nil, fmt.Errorf("Unable to create veth pair %s, %s.", stn.vethHost, err) +// Setup the networking inside the child +// Namely setup the loopback interface +// and the veth interface if requested +func NetSetup(stn *SandboxNetwork) error { + if os.Getpid() != 1 { + panic(errors.New("Cannot use NetSetip from parent.")) } - // Fetch the newly created hostside veth - vethIf, err := net.InterfaceByName(stn.vethHost) - if err != nil { - return nil, fmt.Errorf("Unable to fetch veth pair %s, %s.", stn.vethHost, err) + if err := setupLoopback(stn); err != nil { + return fmt.Errorf("Unable to setup loopback interface: %+v", err) } - // Add the host side veth to the bridge - if err = br.AddSlaveIfc(vethIf); err != nil { - return nil, fmt.Errorf("Unable to add veth pair %s to bridge, %s.", stn.vethHost, err) + // If required configure veth + if stn.VethGuest != "" { + if err := setupVEth(stn); err != nil { + return fmt.Errorf("Unable to setup veth interface: %+v", err) + } } - // Bring the host side veth interface up - if err = veth.SetLinkUp(); err != nil { - return nil, fmt.Errorf("Unable to bring veth pair %s up, %s.", stn.vethHost, err) - } + return nil +} - // Assign the veth path to the namespace - pid := childPid - if err := veth.SetPeerLinkNsPid(pid); err != nil { - return nil, fmt.Errorf("Unable to add veth pair %s to namespace, %s.", stn.vethHost, err) +func setupLoopback(stn *SandboxNetwork) error { + // Bring loopback interface up + lo, err := tenus.NewLinkFrom("lo") + if err != nil { + return fmt.Errorf("Unable to fetch loopback interface, %s.", err) } - // Allocate a new IP address - stn.ip = getFreshIP(htn.min, htn.max, log) - if stn.ip == "" { - return nil, errors.New("Unable to acquire random IP") + // Bring the link up + err = lo.SetLinkUp() + if err != nil { + return fmt.Errorf("Unable to bring loopback interface up, %s.", err) } + + return nil +} - // Parse the ip/class into the the appropriate formats - vethGuestIp, vethGuestIpNet, err := net.ParseCIDR(stn.ip + "/" + htn.class) +func setupVEth(stn *SandboxNetwork) error { + ifc, err := tenus.NewLinkFrom(stn.VethGuest) + if err != nil { - return nil, fmt.Errorf("Unable to parse ip %s, %s.", stn.ip, err) + return fmt.Errorf("Unable to fetch inteface %s, %s.", stn.VethGuest, err) } - - // Set interface address in the namespace - if err := veth.SetPeerLinkNetInNs(pid, vethGuestIp, vethGuestIpNet, nil); err != nil { - return nil, fmt.Errorf("Unable to parse ip link in namespace, %s.", err) + + // Bring the link down to prepare for renaming + if err = ifc.SetLinkDown(); err != nil { + return fmt.Errorf("Unable to bring interface %s down, %s.", stn.VethGuest, err) } - return stn, nil - -} - -// Setup the networking inside the child -// Namely setup the loopback interface -// and the veth interface if requested -func NetSetup(stn *SandboxNetwork, htn *HostNetwork) error { - if os.Getpid() != 1 { - panic(errors.New("Cannot use netChildSetup from child.")) + // Rename the interface to a standard eth0 (not really a necessity) + if err = tenus.RenameInterface(stn.VethGuest, ozDefaultInterfaceInternal); err != nil { + return fmt.Errorf("Unable to rename interface %s, %s.", stn.VethGuest, err) } - // Bring loopback interface up - lo, err := tenus.NewLinkFrom("lo") + // Refetch the interface again as it has changed + ifc, err = tenus.NewLinkFrom(ozDefaultInterfaceInternal) if err != nil { - return fmt.Errorf("Unable to fetch loopback interface, %s.", err) + return fmt.Errorf("Unable to fetch interface %s, %s.", ozDefaultInterfaceInternal, err) } - // Bring the link up - err = lo.SetLinkUp() - if err != nil { - return fmt.Errorf("Unable to bring loopback interface up, %s.", err) + // Bring the link back up + if err = ifc.SetLinkUp(); err != nil { + return fmt.Errorf("Unable to bring interface %s up, %s.", ozDefaultInterfaceInternal, err) } - // If required configure veth - if stn.vethGuest != "" { - ifc, err := tenus.NewLinkFrom(stn.vethGuest) - if err == nil { - // Bring the link down to prepare for renaming - if err = ifc.SetLinkDown(); err != nil { - return fmt.Errorf("Unable to bring interface %s down, %s.", stn.vethGuest, err) - } - - // Rename the interface to a standard eth0 (not really a necessity) - if err = tenus.RenameInterface(stn.vethGuest, ozDefaultInterfaceInternal); err != nil { - return fmt.Errorf("Unable to rename interface %s, %s.", stn.vethGuest, err) - } - - // Refetch the interface again as it has changed - ifc, err = tenus.NewLinkFrom(ozDefaultInterfaceInternal) - if err != nil { - return fmt.Errorf("Unable to fetch interface %s, %s.", ozDefaultInterfaceInternal, err) - } - - // Bring the link back up - if err = ifc.SetLinkUp(); err != nil { - return fmt.Errorf("Unable to bring interface %s up, %s.", ozDefaultInterfaceInternal, err) - } - - // Set the link's default gateway - if err = ifc.SetLinkDefaultGw(&htn.gateway); err != nil { - return fmt.Errorf("Unable to set default route %s.", err) - } - - } else { - return fmt.Errorf("Unable to fetch inteface %s, %s.", stn.vethGuest, err) - } + // Set the link's default gateway + if err = ifc.SetLinkDefaultGw(&stn.Gateway); err != nil { + return fmt.Errorf("Unable to set default route %s.", err) } - + return nil - } // Try to find an unassigned IP address diff --git a/oz-daemon/daemon.go b/oz-daemon/daemon.go index 04a1bb6..6f3f156 100644 --- a/oz-daemon/daemon.go +++ b/oz-daemon/daemon.go @@ -33,7 +33,6 @@ func Main() { d.handlePing, d.handleListProfiles, d.handleLaunch, - d.handleInitBridgeNetwork, d.handleListSandboxes, d.handleClean, d.handleLogs, @@ -64,14 +63,23 @@ func initialize() *daemonState { d.nextSboxId = 1 d.nextDisplay = 100 - d.log.Info("Initializing bridge networking") - htn, err := network.BridgeInit(d.log) - if err != nil { - d.log.Fatalf("Failed to initialize bridge networking: %+v", err) + for _, pp := range d.profiles { + if pp.Networking.Nettype == "bridge" { + d.log.Info("Initializing bridge networking") + htn, err := network.BridgeInit(d.config.BridgeMACAddr, d.config.NMIgnoreFile, d.log) + if err != nil { + d.log.Fatalf("Failed to initialize bridge networking: %+v", err) + return nil + } + + d.network = htn + + network.NetPrint(d.log) + + break; + } } - - d.network = htn - + return d } @@ -80,7 +88,7 @@ func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) { for _, sbox := range d.sandboxes { if sbox.init.Process.Pid == pid { - sbox.remove() + sbox.remove(d.log) return } } @@ -117,7 +125,7 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { return m.Respond(&ErrorMsg{err.Error()}) } d.Debug("Would launch %s", p.Name) - _, err = d.launch(p, m.Ucred.Uid, m.Ucred.Gid) + _, err = d.launch(p, 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()}) @@ -125,12 +133,6 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error { return m.Respond(&OkMsg{}) } -func (d *daemonState) handleInitBridgeNetwork(msg *InitNetworkMsg, m *ipc.Message) error { - d.Debug("Network bridge init message received: %+v", msg) - - return m.Respond(&OkMsg{}) -} - func (d *daemonState) getProfileByIdxOrName(index int, name string) (*oz.Profile, error) { if len(name) == 0 { if index < 1 || index > len(d.profiles) { diff --git a/oz-daemon/launch.go b/oz-daemon/launch.go index ae55d91..5214275 100644 --- a/oz-daemon/launch.go +++ b/oz-daemon/launch.go @@ -3,10 +3,6 @@ package daemon import ( "bufio" "fmt" - "github.com/subgraph/oz" - "github.com/subgraph/oz/fs" - "github.com/subgraph/oz/xpra" - "github.com/subgraph/oz/network" "io" "os" "os/exec" @@ -14,6 +10,13 @@ import ( "path" "sync" "syscall" + + "github.com/subgraph/oz" + "github.com/subgraph/oz/fs" + "github.com/subgraph/oz/xpra" + "github.com/subgraph/oz/network" + + "github.com/op/go-logging" ) const initPath = "/usr/local/bin/oz-init" @@ -43,9 +46,10 @@ func findSandbox(id int) *Sandbox { return nil } */ + const initCloneFlags = syscall.CLONE_NEWNS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWUTS | syscall.CLONE_NEWNET -func createInitCommand(name, chroot string, uid uint32, display int) *exec.Cmd { +func createInitCommand(name, chroot string, uid uint32, display int, stn *network.SandboxNetwork) *exec.Cmd { cmd := exec.Command(initPath) cmd.Dir = "/" cmd.SysProcAttr = &syscall.SysProcAttr{ @@ -56,38 +60,59 @@ func createInitCommand(name, chroot string, uid uint32, display int) *exec.Cmd { "INIT_PROFILE=" + name, fmt.Sprintf("INIT_UID=%d", uid), } + + if stn.Ip != "" { + cmd.Env = append(cmd.Env, "INIT_ADDR=" + stn.Ip) + cmd.Env = append(cmd.Env, "INIT_VHOST=" + stn.VethHost) + cmd.Env = append(cmd.Env, "INIT_VGUEST=" + stn.VethGuest) + cmd.Env = append(cmd.Env, "INIT_GATEWAY=" + stn.Gateway.String() + "/" + stn.Class) + } + if display > 0 { cmd.Env = append(cmd.Env, fmt.Sprintf("INIT_DISPLAY=%d", display)) } + return cmd } -func (d *daemonState) launch(p *oz.Profile, uid, gid uint32) (*Sandbox, error) { +func (d *daemonState) launch(p *oz.Profile, 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) } fs := fs.NewFromProfile(p, u, d.config.SandboxPath, d.log) - if err := fs.Setup(); err != nil { + if err := fs.Setup(d.config.ProfileDir); err != nil { return nil, err } display := 0 - if p.XServer.Enabled { + if p.XServer.Enabled && p.Networking.Nettype == "host" { display = d.nextDisplay d.nextDisplay += 1 } - - cmd := createInitCommand(p.Name, fs.Root(), uid, display) + + stn := new(network.SandboxNetwork) + if p.Networking.Nettype == "bridge" { + stn, err = network.PrepareSandboxNetwork(d.network, log) + if err != nil { + return nil, fmt.Errorf("Unable to prepare veth network: %+v", err) + } + } + + cmd := createInitCommand(p.Name, fs.Root(), uid, display, stn) + log.Debug("Command environment: %+v", cmd.Env) pp, err := cmd.StderrPipe() if err != nil { fs.Cleanup() return nil, fmt.Errorf("error creating stderr pipe for init process: %v", err) } + if err := cmd.Start(); err != nil { fs.Cleanup() - return nil, err + return nil, fmt.Errorf("Unable to start process: %+v", err) } + + sbox := &Sandbox{ daemon: d, id: d.nextSboxId, @@ -98,7 +123,17 @@ func (d *daemonState) launch(p *oz.Profile, uid, gid uint32) (*Sandbox, error) { fs: fs, addr: path.Join(fs.Root(), "tmp", "oz-init-control"), stderr: pp, + network: stn, + } + + if p.Networking.Nettype == "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 sbox.profile.XServer.Enabled { @@ -112,11 +147,14 @@ func (d *daemonState) launch(p *oz.Profile, uid, gid uint32) (*Sandbox, error) { return sbox, nil } -func (sbox *Sandbox) remove() { +func (sbox *Sandbox) remove(log *logging.Logger) { sboxes := []*Sandbox{} for _, sb := range sbox.daemon.sandboxes { if sb == sbox { sb.fs.Cleanup() + if sb.profile.Networking.Nettype == "bridge" { + sb.network.Cleanup(log) + } } else { sboxes = append(sboxes, sb) } diff --git a/oz-daemon/protocol.go b/oz-daemon/protocol.go index 6e724e0..a5a3500 100644 --- a/oz-daemon/protocol.go +++ b/oz-daemon/protocol.go @@ -35,11 +35,6 @@ type LaunchMsg struct { Name string } -type InitNetworkMsg struct { - Index int "NetworkBridge" - Name string -} - type ListSandboxesMsg struct { _ string "ListSandboxes" } diff --git a/oz-init/init.go b/oz-init/init.go index 0ff25ec..e1ceadb 100644 --- a/oz-init/init.go +++ b/oz-init/init.go @@ -3,13 +3,8 @@ package ozinit import ( "bufio" "fmt" - "github.com/kr/pty" - "github.com/op/go-logging" - "github.com/subgraph/oz" - "github.com/subgraph/oz/fs" - "github.com/subgraph/oz/ipc" - "github.com/subgraph/oz/xpra" "io" + "net" "os" "os/exec" "os/user" @@ -17,6 +12,15 @@ import ( "strings" "sync" "syscall" + + "github.com/subgraph/oz" + "github.com/subgraph/oz/fs" + "github.com/subgraph/oz/ipc" + "github.com/subgraph/oz/xpra" + "github.com/subgraph/oz/network" + + "github.com/kr/pty" + "github.com/op/go-logging" ) const SocketAddress = "/tmp/oz-init-control" @@ -31,6 +35,7 @@ type initState struct { display int fs *fs.Filesystem xpraReady sync.WaitGroup + network *network.SandboxNetwork } // By convention oz-init writes log messages to stderr with a single character @@ -62,7 +67,12 @@ func parseArgs() *initState { pname := getvar("INIT_PROFILE") uidval := getvar("INIT_UID") dispval := os.Getenv("INIT_DISPLAY") - + + stnip := os.Getenv("INIT_ADDR") + stnvhost := os.Getenv("INIT_VHOST") + stnvguest := os.Getenv("INIT_VGUEST") + stngateway := os.Getenv("INIT_GATEWAY") + var config *oz.Config config, err := oz.LoadConfig(oz.DefaultConfigPath) if err != nil { @@ -99,7 +109,21 @@ func parseArgs() *initState { } display = d } - + + stn := new(network.SandboxNetwork) + if stnip != "" { + gateway, _, err := net.ParseCIDR(stngateway) + if err != nil { + log.Error("Unable to parse network configuration gateway (%s): %v", stngateway, err) + os.Exit(1) + } + + stn.Ip = stnip + stn.VethHost = stnvhost + stn.VethGuest = stnvguest + stn.Gateway = gateway + } + return &initState{ log: log, config: config, @@ -109,11 +133,23 @@ func parseArgs() *initState { user: u, display: display, fs: fs.NewFromProfile(p, u, config.SandboxPath, log), + network: stn, } } func (st *initState) runInit() { st.log.Info("Starting oz-init for profile: %s", st.profile.Name) + + if st.profile.Networking.Nettype != "host" { + //NetSetup(stn *SandboxNetwork, htn *HostNetwork) error + err := network.NetSetup(st.network) + if err != nil { + st.log.Error("Unable to setup networking: %+v", err) + os.Exit(1) + } + } + network.NetPrint(st.log) + if syscall.Sethostname([]byte(st.profile.Name)) != nil { st.log.Error("Failed to set hostname to (%s)", st.profile.Name) } @@ -124,7 +160,7 @@ func (st *initState) runInit() { os.Exit(1) } oz.ReapChildProcs(st.log, st.handleChildExit) - + if st.profile.XServer.Enabled { if st.display == 0 { st.log.Error("Cannot start xpra because no display number was passed to oz-init") @@ -204,7 +240,7 @@ func (st *initState) readXpraOutput(r io.ReadCloser) { } func (st *initState) launchApplication() { - cmd := exec.Command(st.profile.Path) + cmd := exec.Command(st.profile.Path + ".unsafe") stdout, err := cmd.StdoutPipe() if err != nil { st.log.Warning("Failed to create stdout pipe: %v", err) @@ -260,7 +296,7 @@ func (st *initState) handleRunShell(rs *RunShellMsg, msg *ipc.Message) error { if msg.Ucred == nil { return msg.Respond(&ErrorMsg{"No credentials received for RunShell command"}) } - if msg.Ucred.Uid == 0 || msg.Ucred.Gid == 0 && !st.config.AllowRootShell { + if (msg.Ucred.Uid == 0 || msg.Ucred.Gid == 0) && st.config.AllowRootShell != true { return msg.Respond(&ErrorMsg{"Cannot open shell because allowRootShell is disabled"}) } st.log.Info("Starting shell with uid = %d, gid = %d", msg.Ucred.Uid, msg.Ucred.Gid)