Working networking and minor fixes (mainly config flags)

master
xSmurf 10 years ago
parent 41b2e9ded5
commit f7fcf24f71

@ -6,11 +6,13 @@ import (
) )
type Config struct { type Config struct {
ProfileDir string `json:"profile_dir"` ProfileDir string `json:"profile_dir"`
ShellPath string `json:"shell_path"` ShellPath string `json:"shell_path"`
SandboxPath string `json:"sandbox_path"` SandboxPath string `json:"sandbox_path"`
AllowRootShell bool `json:"allow_root_shell"` BridgeMACAddr string `json:"bridge_mac"`
LogXpra bool `json:"log_xpra"` NMIgnoreFile string `json:"nm_ignore_file"`
AllowRootShell bool `json:"allow_root_shell"`
LogXpra bool `json:"log_xpra"`
} }
const DefaultConfigPath = "/etc/oz/oz.conf" const DefaultConfigPath = "/etc/oz/oz.conf"
@ -20,6 +22,8 @@ func NewDefaultConfig() *Config {
ProfileDir: "/var/lib/oz/cells.d", ProfileDir: "/var/lib/oz/cells.d",
ShellPath: "/bin/bash", ShellPath: "/bin/bash",
SandboxPath: "/srv/oz", SandboxPath: "/srv/oz",
NMIgnoreFile: "/etc/NetworkManager/conf.d/oz.conf",
BridgeMACAddr: "6A:A8:2E:56:E8:9C",
AllowRootShell: false, AllowRootShell: false,
LogXpra: false, LogXpra: false,
} }

@ -116,7 +116,7 @@ func (mi *mountItem) bindItem() error {
target := mi.targetPath() target := mi.targetPath()
_, err = os.Stat(target) _, err = os.Stat(target)
if err == nil || !os.IsNotExist(err) { 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 return nil
} }
if sinfo.IsDir() { if sinfo.IsDir() {

@ -11,16 +11,16 @@ import (
) )
var basicBindDirs = []string{ var basicBindDirs = []string{
"/bin", "/lib", "/lib64", "/usr", "/etc", "/var/lib/oz", "/bin", "/lib", "/lib64", "/usr", "/etc",
} }
var basicEmptyDirs = []string{ var basicEmptyDirs = []string{
"/sbin", "/var", "/var/lib", "/sbin", "/var", "/var/lib",
"/var/cache", "/home", "/boot", "/var/cache", "/home", "/boot",
"/tmp", "/run", "/run/user", "/tmp", "/run", "/run/user",
"/run/lock", "/root", "/opt", "/run/shm", "/run/lock", "/root",
"/srv", "/dev", "/proc", "/sys", "/opt", "/srv", "/dev", "/proc",
"/mnt", "/media", "/sys", "/mnt", "/media",
} }
var basicBlacklist = []string{ var basicBlacklist = []string{
@ -39,7 +39,19 @@ var basicSymlinks = [][2]string{
{"/run/lock", "/var/lock"}, {"/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 fs.xpra != "" {
if err := fs.createXpraDir(); err != nil { if err := fs.createXpraDir(); err != nil {
return err return err

@ -4,122 +4,232 @@ import(
//Builtin //Builtin
"errors" "errors"
"fmt" "fmt"
//"math" "math/rand"
"net" "net"
"os" "os"
//"os/exec"
"strings" "strings"
"time"
//Internal //Internal
"github.com/op/go-logging"
//External //External
"github.com/milosgajdos83/tenus" "github.com/milosgajdos83/tenus"
"github.com/op/go-logging"
) )
func BridgeInit(bridgeMAC string, nmIgnoreFile string, log *logging.Logger) (*HostNetwork, error) {
func BridgeInit(log *logging.Logger) (*HostNetwork, error) {
htn := new(HostNetwork)
if os.Getpid() == 1 { if os.Getpid() == 1 {
panic(errors.New("Cannot use netinit from child.")) panic(errors.New("Cannot use netinit from child."))
} }
// Fetch the bridge interface by ifname htn := &HostNetwork{
brL, err := net.InterfaceByName(ozDefaultInterfaceBridge) 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 { if err != nil {
log.Info("Bridge not found, attempting to create a new one") log.Info("Bridge not found, attempting to create a new one")
_, err = createNewBridge(log) br, err = tenus.NewBridgeWithName(ozDefaultInterfaceBridge)
if err != nil { if err != nil {
return nil, fmt.Errorf("Unable to create bridge %+v", err) 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 if err:= htn.configureBridgeInterface(br, log); err != nil {
addrs, _ := brL.Addrs() return nil, err
if len(addrs) == 0 {
return nil, errors.New("Host bridge does not have an IP address assigned")
} }
// Try to build the network config from the bridge's address brL := br.NetInterface()
addrIndex := -1 addrs, err := brL.Addrs()
for i, addr := range addrs { if err != nil {
bIP, brIP, _ := net.ParseCIDR(addr.String()) return nil, fmt.Errorf("Unable to get bridgre interface addresses: %+v", err)
}
// Discard IPv6 (TODO...) // Build the ip range which we will use for the network
if bIP.To4() != nil { if err := htn.buildBridgeNetwork(addrs); err != nil {
addrIndex = i return nil, err
}
bMask := []byte(brIP.Mask) return htn, nil
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) func PrepareSandboxNetwork(htn *HostNetwork, log *logging.Logger) (*SandboxNetwork, error) {
htn.min++ stn := new(SandboxNetwork)
htn.max = inet_aton(htn.broadcast) stn.VethHost = tenus.MakeNetInterfaceName(ozDefaultInterfacePrefix)
htn.max-- stn.VethGuest = stn.VethHost + "1"
break 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")
} }
if addrIndex < 0 { return stn, nil
return nil, errors.New("Could not find IPv4 for bridge interface") }
func NetInit(stn *SandboxNetwork, htn *HostNetwork, childPid int, log *logging.Logger) error {
if os.Getpid() == 1 {
panic(errors.New("Cannot use netSetup from child."))
} }
return htn, nil // 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' with: %s", stn.VethHost, stn.Ip + "/" + htn.Class)
// Create a new bridge on the host // Fetch the bridge from the ifname
// Assign it an unused range br, err := tenus.BridgeFromName(ozDefaultInterfaceBridge)
// And bring the interface up if err != nil {
func createNewBridge(log *logging.Logger) (tenus.Bridger, error) { return fmt.Errorf("Unable to attach to bridge interface %, %s.", ozDefaultInterfaceBridge, err)
if os.Getpid() == 1 {
panic(errors.New("Cannot use netinit from child."))
} }
// Create the bridge // Make sure the bridge is configured and the link is up
br, err := tenus.NewBridgeWithName(ozDefaultInterfaceBridge) // 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 { if err != nil {
return nil, err return fmt.Errorf("Unable to create veth pair %s, %s.", stn.VethHost, err)
} }
// Lookup an empty ip range // Fetch the newly created hostside veth
brIp, brIpNet, err := findEmptyRange(log) vethIf, err := net.InterfaceByName(stn.VethHost)
if err != nil { if err != nil {
return nil, errors.New("Could not find an ip range to assign to the bridge") return fmt.Errorf("Unable to fetch veth pair %s, %s.", stn.VethHost, err)
} }
// Setup the bridge's address // Add the host side veth to the bridge
if err := br.SetLinkIp(brIp, brIpNet); err != nil { if err := br.AddSlaveIfc(vethIf); err != nil {
return nil, err return fmt.Errorf("Unable to add veth pair %s to bridge, %s.", stn.VethHost, err)
} }
// Bridge the interface up // Bring the host side veth interface up
if err = br.SetLinkUp(); err != nil { if err := veth.SetLinkUp(); err != nil {
return nil, fmt.Errorf("Unable to bring bridge '%+v' up: %+v", ozDefaultInterfaceBridge, err) return fmt.Errorf("Unable to bring veth pair %s up, %s.", stn.VethHost, err)
}
// 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)
}
// Parse the ip/class into the the appropriate formats
vethGuestIp, vethGuestIpNet, err := net.ParseCIDR(stn.Ip + "/" + htn.Class)
if err != nil {
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)
} }
return br, nil 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)
}
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 fmt.Errorf("Unable to bring bridge '%+v' up: %+v", ozDefaultInterfaceBridge, err)
}
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 // 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 // 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 { type localNet struct {
min uint64 min uint64
max uint64 max uint64
@ -186,10 +296,9 @@ func findEmptyRange(log *logging.Logger) (net.IP, *net.IPNet, error) {
bMin = bMax - 0xFE bMin = bMax - 0xFE
} }
// XXX
availableRange = inet_ntoa(bMin).String() + "/24" availableRange = inet_ntoa(bMin).String() + "/24"
log.Info("Found available range: %+v", availableRange)
return net.ParseCIDR(availableRange) return net.ParseCIDR(availableRange)
} }

@ -2,9 +2,12 @@ package network
import ( import (
//Builtin //Builtin
"fmt"
"net" "net"
"strings" "strings"
"strconv" "strconv"
"github.com/op/go-logging"
) )
const ( const (
@ -16,32 +19,35 @@ const (
) )
type HostNetwork struct { type HostNetwork struct {
// Host bridge IP address
hostip net.IP
// Gateway ip (bridge ip) // Gateway ip (bridge ip)
gateway net.IP Gateway net.IP
// Gateway ip (bridge ip)
GatewayNet *net.IPNet
// Bridge netmask // Bridge netmask
netmask net.IP Netmask net.IP
// Broadcast ip // Broadcast ip
broadcast net.IP Broadcast net.IP
// IP class (ie: /24) // IP class (ie: /24)
class string Class string
// Minimum longip available ip // Minimum longip available ip
min uint64 Min uint64
// Maximum longip available ip // Maximum longip available ip
max uint64 Max uint64
// Bridge interface MAC Address
BridgeMAC string
} }
type SandboxNetwork struct { type SandboxNetwork struct {
// Name of the veth in the host // Name of the veth in the host
vethHost string VethHost string
// Temporary name of the guest' veth in the host // Temporary name of the guest' veth in the host
vethGuest string VethGuest string
// Guest ip address // Guest ip address
ip string Ip string
// Gateway ip (bridge ip)
host *HostNetwork Gateway net.IP
// IP class (ie: /24)
Class string
} }
var privateNetworkRanges []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 // Convert longip to net.IP
func inet_ntoa(ipnr uint64) net.IP { func inet_ntoa(ipnr uint64) net.IP {
var bytes [4]byte var bytes [4]byte

@ -4,145 +4,90 @@ import (
//Builtin //Builtin
"errors" "errors"
"fmt" "fmt"
//"math"
"math/rand" "math/rand"
"net"
"os" "os"
"time" "time"
// Internal
"github.com/op/go-logging"
//External //External
"github.com/j-keck/arping" "github.com/j-keck/arping"
"github.com/milosgajdos83/tenus" "github.com/milosgajdos83/tenus"
"github.com/op/go-logging"
) )
func NetInit(log *logging.Logger, htn *HostNetwork, childPid int) (*SandboxNetwork, error) { // Setup the networking inside the child
if os.Getpid() == 1 { // Namely setup the loopback interface
panic(errors.New("Cannot use netSetup from child.")) // and the veth interface if requested
func NetSetup(stn *SandboxNetwork) error {
if os.Getpid() != 1 {
panic(errors.New("Cannot use NetSetip from parent."))
} }
stn := new(SandboxNetwork) if err := setupLoopback(stn); err != nil {
return fmt.Errorf("Unable to setup loopback interface: %+v", err)
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 // If required configure veth
veth, err := tenus.NewVethPairWithOptions(stn.vethHost, tenus.VethOptions{PeerName: stn.vethGuest}) if stn.VethGuest != "" {
if err != nil { if err := setupVEth(stn); err != nil {
return nil, fmt.Errorf("Unable to create veth pair %s, %s.", stn.vethHost, err) return fmt.Errorf("Unable to setup veth interface: %+v", err)
}
} }
// Fetch the newly created hostside veth return nil
vethIf, err := net.InterfaceByName(stn.vethHost) }
if err != nil {
return nil, fmt.Errorf("Unable to fetch veth pair %s, %s.", stn.vethHost, err)
}
// Add the host side veth to the bridge func setupLoopback(stn *SandboxNetwork) error {
if err = br.AddSlaveIfc(vethIf); err != nil { // Bring loopback interface up
return nil, fmt.Errorf("Unable to add veth pair %s to bridge, %s.", stn.vethHost, err) lo, err := tenus.NewLinkFrom("lo")
if err != nil {
return fmt.Errorf("Unable to fetch loopback interface, %s.", err)
} }
// Bring the host side veth interface up // Bring the link up
if err = veth.SetLinkUp(); err != nil { err = lo.SetLinkUp()
return nil, fmt.Errorf("Unable to bring veth pair %s up, %s.", stn.vethHost, err) if err != nil {
return fmt.Errorf("Unable to bring loopback interface up, %s.", err)
} }
// Assign the veth path to the namespace return nil
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)
}
// Allocate a new IP address func setupVEth(stn *SandboxNetwork) error {
stn.ip = getFreshIP(htn.min, htn.max, log) ifc, err := tenus.NewLinkFrom(stn.VethGuest)
if stn.ip == "" {
return nil, errors.New("Unable to acquire random IP")
}
// Parse the ip/class into the the appropriate formats
vethGuestIp, vethGuestIpNet, err := net.ParseCIDR(stn.ip + "/" + htn.class)
if err != nil { 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 // Bring the link down to prepare for renaming
if err := veth.SetPeerLinkNetInNs(pid, vethGuestIp, vethGuestIpNet, nil); err != nil { if err = ifc.SetLinkDown(); err != nil {
return nil, fmt.Errorf("Unable to parse ip link in namespace, %s.", err) return fmt.Errorf("Unable to bring interface %s down, %s.", stn.VethGuest, err)
} }
return stn, nil // 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)
// 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."))
} }
// Bring loopback interface up // Refetch the interface again as it has changed
lo, err := tenus.NewLinkFrom("lo") ifc, err = tenus.NewLinkFrom(ozDefaultInterfaceInternal)
if err != nil { 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 // Bring the link back up
err = lo.SetLinkUp() if err = ifc.SetLinkUp(); err != nil {
if err != nil { return fmt.Errorf("Unable to bring interface %s up, %s.", ozDefaultInterfaceInternal, err)
return fmt.Errorf("Unable to bring loopback interface up, %s.", err)
} }
// If required configure veth // Set the link's default gateway
if stn.vethGuest != "" { if err = ifc.SetLinkDefaultGw(&stn.Gateway); err != nil {
ifc, err := tenus.NewLinkFrom(stn.vethGuest) return fmt.Errorf("Unable to set default route %s.", err)
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)
}
} }
return nil return nil
} }
// Try to find an unassigned IP address // Try to find an unassigned IP address

@ -33,7 +33,6 @@ func Main() {
d.handlePing, d.handlePing,
d.handleListProfiles, d.handleListProfiles,
d.handleLaunch, d.handleLaunch,
d.handleInitBridgeNetwork,
d.handleListSandboxes, d.handleListSandboxes,
d.handleClean, d.handleClean,
d.handleLogs, d.handleLogs,
@ -64,13 +63,22 @@ func initialize() *daemonState {
d.nextSboxId = 1 d.nextSboxId = 1
d.nextDisplay = 100 d.nextDisplay = 100
d.log.Info("Initializing bridge networking") for _, pp := range d.profiles {
htn, err := network.BridgeInit(d.log) if pp.Networking.Nettype == "bridge" {
if err != nil { d.log.Info("Initializing bridge networking")
d.log.Fatalf("Failed to initialize bridge networking: %+v", err) 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
d.network = htn network.NetPrint(d.log)
break;
}
}
return d return d
} }
@ -80,7 +88,7 @@ func (d *daemonState) handleChildExit(pid int, wstatus syscall.WaitStatus) {
for _, sbox := range d.sandboxes { for _, sbox := range d.sandboxes {
if sbox.init.Process.Pid == pid { if sbox.init.Process.Pid == pid {
sbox.remove() sbox.remove(d.log)
return return
} }
} }
@ -117,7 +125,7 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error {
return m.Respond(&ErrorMsg{err.Error()}) return m.Respond(&ErrorMsg{err.Error()})
} }
d.Debug("Would launch %s", p.Name) 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 { if err != nil {
d.Warning("launch of %s failed: %v", p.Name, err) d.Warning("launch of %s failed: %v", p.Name, err)
return m.Respond(&ErrorMsg{err.Error()}) return m.Respond(&ErrorMsg{err.Error()})
@ -125,12 +133,6 @@ func (d *daemonState) handleLaunch(msg *LaunchMsg, m *ipc.Message) error {
return m.Respond(&OkMsg{}) 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) { func (d *daemonState) getProfileByIdxOrName(index int, name string) (*oz.Profile, error) {
if len(name) == 0 { if len(name) == 0 {
if index < 1 || index > len(d.profiles) { if index < 1 || index > len(d.profiles) {

@ -3,10 +3,6 @@ package daemon
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"github.com/subgraph/oz"
"github.com/subgraph/oz/fs"
"github.com/subgraph/oz/xpra"
"github.com/subgraph/oz/network"
"io" "io"
"os" "os"
"os/exec" "os/exec"
@ -14,6 +10,13 @@ import (
"path" "path"
"sync" "sync"
"syscall" "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" const initPath = "/usr/local/bin/oz-init"
@ -43,9 +46,10 @@ func findSandbox(id int) *Sandbox {
return nil return nil
} }
*/ */
const initCloneFlags = syscall.CLONE_NEWNS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWUTS | syscall.CLONE_NEWNET 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 := exec.Command(initPath)
cmd.Dir = "/" cmd.Dir = "/"
cmd.SysProcAttr = &syscall.SysProcAttr{ cmd.SysProcAttr = &syscall.SysProcAttr{
@ -56,38 +60,59 @@ func createInitCommand(name, chroot string, uid uint32, display int) *exec.Cmd {
"INIT_PROFILE=" + name, "INIT_PROFILE=" + name,
fmt.Sprintf("INIT_UID=%d", uid), 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 { if display > 0 {
cmd.Env = append(cmd.Env, fmt.Sprintf("INIT_DISPLAY=%d", display)) cmd.Env = append(cmd.Env, fmt.Sprintf("INIT_DISPLAY=%d", display))
} }
return cmd 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)) u, err := user.LookupId(fmt.Sprintf("%d", uid))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to lookup user for uid=%d: %v", uid, err) return nil, fmt.Errorf("failed to lookup user for uid=%d: %v", uid, err)
} }
fs := fs.NewFromProfile(p, u, d.config.SandboxPath, d.log) 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 return nil, err
} }
display := 0 display := 0
if p.XServer.Enabled { if p.XServer.Enabled && p.Networking.Nettype == "host" {
display = d.nextDisplay display = d.nextDisplay
d.nextDisplay += 1 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() pp, err := cmd.StderrPipe()
if err != nil { if err != nil {
fs.Cleanup() fs.Cleanup()
return nil, fmt.Errorf("error creating stderr pipe for init process: %v", err) return nil, fmt.Errorf("error creating stderr pipe for init process: %v", err)
} }
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
fs.Cleanup() fs.Cleanup()
return nil, err return nil, fmt.Errorf("Unable to start process: %+v", err)
} }
sbox := &Sandbox{ sbox := &Sandbox{
daemon: d, daemon: d,
id: d.nextSboxId, id: d.nextSboxId,
@ -98,7 +123,17 @@ func (d *daemonState) launch(p *oz.Profile, uid, gid uint32) (*Sandbox, error) {
fs: fs, fs: fs,
addr: path.Join(fs.Root(), "tmp", "oz-init-control"), addr: path.Join(fs.Root(), "tmp", "oz-init-control"),
stderr: pp, 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) sbox.ready.Add(1)
go sbox.logMessages() go sbox.logMessages()
if sbox.profile.XServer.Enabled { if sbox.profile.XServer.Enabled {
@ -112,11 +147,14 @@ func (d *daemonState) launch(p *oz.Profile, uid, gid uint32) (*Sandbox, error) {
return sbox, nil return sbox, nil
} }
func (sbox *Sandbox) remove() { func (sbox *Sandbox) remove(log *logging.Logger) {
sboxes := []*Sandbox{} sboxes := []*Sandbox{}
for _, sb := range sbox.daemon.sandboxes { for _, sb := range sbox.daemon.sandboxes {
if sb == sbox { if sb == sbox {
sb.fs.Cleanup() sb.fs.Cleanup()
if sb.profile.Networking.Nettype == "bridge" {
sb.network.Cleanup(log)
}
} else { } else {
sboxes = append(sboxes, sb) sboxes = append(sboxes, sb)
} }

@ -35,11 +35,6 @@ type LaunchMsg struct {
Name string Name string
} }
type InitNetworkMsg struct {
Index int "NetworkBridge"
Name string
}
type ListSandboxesMsg struct { type ListSandboxesMsg struct {
_ string "ListSandboxes" _ string "ListSandboxes"
} }

@ -3,13 +3,8 @@ package ozinit
import ( import (
"bufio" "bufio"
"fmt" "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" "io"
"net"
"os" "os"
"os/exec" "os/exec"
"os/user" "os/user"
@ -17,6 +12,15 @@ import (
"strings" "strings"
"sync" "sync"
"syscall" "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" const SocketAddress = "/tmp/oz-init-control"
@ -31,6 +35,7 @@ type initState struct {
display int display int
fs *fs.Filesystem fs *fs.Filesystem
xpraReady sync.WaitGroup xpraReady sync.WaitGroup
network *network.SandboxNetwork
} }
// By convention oz-init writes log messages to stderr with a single character // By convention oz-init writes log messages to stderr with a single character
@ -63,6 +68,11 @@ func parseArgs() *initState {
uidval := getvar("INIT_UID") uidval := getvar("INIT_UID")
dispval := os.Getenv("INIT_DISPLAY") 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 var config *oz.Config
config, err := oz.LoadConfig(oz.DefaultConfigPath) config, err := oz.LoadConfig(oz.DefaultConfigPath)
if err != nil { if err != nil {
@ -100,6 +110,20 @@ func parseArgs() *initState {
display = d 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{ return &initState{
log: log, log: log,
config: config, config: config,
@ -109,11 +133,23 @@ func parseArgs() *initState {
user: u, user: u,
display: display, display: display,
fs: fs.NewFromProfile(p, u, config.SandboxPath, log), fs: fs.NewFromProfile(p, u, config.SandboxPath, log),
network: stn,
} }
} }
func (st *initState) runInit() { func (st *initState) runInit() {
st.log.Info("Starting oz-init for profile: %s", st.profile.Name) 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 { if syscall.Sethostname([]byte(st.profile.Name)) != nil {
st.log.Error("Failed to set hostname to (%s)", st.profile.Name) st.log.Error("Failed to set hostname to (%s)", st.profile.Name)
} }
@ -204,7 +240,7 @@ func (st *initState) readXpraOutput(r io.ReadCloser) {
} }
func (st *initState) launchApplication() { func (st *initState) launchApplication() {
cmd := exec.Command(st.profile.Path) cmd := exec.Command(st.profile.Path + ".unsafe")
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
st.log.Warning("Failed to create stdout pipe: %v", err) 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 { if msg.Ucred == nil {
return msg.Respond(&ErrorMsg{"No credentials received for RunShell command"}) 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"}) 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) st.log.Info("Starting shell with uid = %d, gid = %d", msg.Ucred.Uid, msg.Ucred.Gid)

Loading…
Cancel
Save