diff --git a/config.go b/config.go new file mode 100644 index 0000000..5d59e9e --- /dev/null +++ b/config.go @@ -0,0 +1,36 @@ +package oz + +import ( + "encoding/json" + "io/ioutil" +) + +type Config struct { + ProfileDir string `json:"profile_dir"` + ShellPath string `json:"shell_path"` + AllowRootShell bool `json:"allow_root_shell"` + LogXpra bool `json:"log_xpra"` +} + +const DefaultConfigPath = "/etc/oz/oz.conf" + +func NewDefaultConfig() *Config { + return &Config{ + ProfileDir: "/var/lib/oz/cells.d", + ShellPath: "/bin/bash", + AllowRootShell: false, + LogXpra: false, + } +} + +func LoadConfig(path string) (*Config, error) { + bs, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + c := NewDefaultConfig() + if err := json.Unmarshal(bs, c); err != nil { + return nil, err + } + return c, nil +} diff --git a/oz-daemon/daemon.go b/oz-daemon/daemon.go index 466146c..af9d36a 100644 --- a/oz-daemon/daemon.go +++ b/oz-daemon/daemon.go @@ -13,6 +13,7 @@ import ( type daemonState struct { log *logging.Logger + config *oz.Config profiles oz.Profiles sandboxes []*Sandbox nextSboxId int @@ -41,7 +42,14 @@ func Main() { func initialize() *daemonState { d := &daemonState{} d.initializeLogging() - ps, err := oz.LoadProfiles("/var/lib/oz/cells.d") + var config *oz.Config + config, err := oz.LoadConfig(oz.DefaultConfigPath) + if err != nil { + d.log.Info("Could not load config file (%s), using default config", oz.DefaultConfigPath) + config = oz.NewDefaultConfig() + } + d.config = config + ps, err := oz.LoadProfiles(config.ProfileDir) if err != nil { d.log.Fatalf("Failed to load profiles: %v", err) } diff --git a/oz-init/init.go b/oz-init/init.go index 65e76ec..9d4c809 100644 --- a/oz-init/init.go +++ b/oz-init/init.go @@ -18,12 +18,12 @@ import ( "syscall" ) -const profileDirectory = "/var/lib/oz/cells.d" -const socketAddress = "/tmp/oz-init-control" +const SocketAddress = "/tmp/oz-init-control" type initState struct { log *logging.Logger profile *oz.Profile + config *oz.Config uid int gid int user *user.User @@ -43,12 +43,8 @@ func createLogger() *logging.Logger { return l } -var allowRootShell = false -var logXpra = true - func Main() { - st := parseArgs() - st.runInit() + parseArgs().runInit() } func parseArgs() *initState { @@ -65,7 +61,14 @@ func parseArgs() *initState { uidval := getvar("INIT_UID") dispval := os.Getenv("INIT_DISPLAY") - p, err := loadProfile(pname) + var config *oz.Config + config, err := oz.LoadConfig(oz.DefaultConfigPath) + if err != nil { + log.Info("Could not load config file (%s), using default config", oz.DefaultConfigPath) + config = oz.NewDefaultConfig() + } + + p, err := loadProfile(config.ProfileDir, pname) if err != nil { log.Error("Could not load profile %s: %v", pname, err) os.Exit(1) @@ -97,6 +100,7 @@ func parseArgs() *initState { return &initState{ log: log, + config: config, profile: p, uid: uid, gid: gid, @@ -127,7 +131,7 @@ func (st *initState) runInit() { oz.ReapChildProcs(st.log, st.handleChildExit) - s, err := ipc.NewServer(socketAddress, messageFactory, st.log, + s, err := ipc.NewServer(SocketAddress, messageFactory, st.log, handlePing, st.handleRunShell, ) @@ -135,7 +139,7 @@ func (st *initState) runInit() { st.log.Error("NewServer failed: %v", err) os.Exit(1) } - if err := os.Chown(socketAddress, st.uid, st.gid); err != nil { + if err := os.Chown(SocketAddress, st.uid, st.gid); err != nil { st.log.Warning("Failed to chown oz-init control socket: %v", err) } @@ -179,20 +183,20 @@ func (st *initState) readXpraOutput(r io.ReadCloser) { if len(line) > 0 { if strings.Contains(line, "xpra is ready.") { os.Stderr.WriteString("XPRA READY\n") - if !logXpra { + if !st.config.LogXpra { r.Close() return } } - if logXpra { + if st.config.LogXpra { st.log.Debug("(xpra) %s", line) } } } } -func loadProfile(name string) (*oz.Profile, error) { - ps, err := oz.LoadProfiles(profileDirectory) +func loadProfile(dir, name string) (*oz.Profile, error) { + ps, err := oz.LoadProfiles(dir) if err != nil { return nil, err } @@ -212,11 +216,11 @@ 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 && !allowRootShell { + if msg.Ucred.Uid == 0 || msg.Ucred.Gid == 0 && !st.config.AllowRootShell { 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) - cmd := exec.Command("/bin/sh", "-i") + cmd := exec.Command(st.config.ShellPath, "-i") cmd.SysProcAttr = &syscall.SysProcAttr{} cmd.SysProcAttr.Credential = &syscall.Credential{ Uid: msg.Ucred.Uid,