diff --git a/cmd/start.go b/cmd/start.go index fafffe7..c8c3639 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/c-robinson/iplib" log "github.com/sirupsen/logrus" - "io/ioutil" "net" "os" "regexp" @@ -13,21 +12,18 @@ import ( "strings" ) -// FIXME : Do not work?! func setJailConfigUpdated(jail *Jail) error { if len(jail.ConfigPath) == 0 { return errors.New(fmt.Sprintf("No config path for jail %s", jail.Name)) } - for i, j := range gJails { - if jail.Name == j.Name { - fmt.Printf("Tag %s as configUpdated\n", jail.Name) - gJails[i].ConfigUpdated = true - return nil - } + j, err := getJailFromArray(jail.Name, gJails) + if err != nil { + return err } - - return errors.New("Jail not found") + j.ConfigUpdated = true + + return nil } func mountProcFs(jail *Jail) error { @@ -194,12 +190,18 @@ func mountAllJailFsFromHost(jail *Jail) error { func prepareJailedZfsDatasets(jail *Jail) error { if jail.Config.Jail_zfs > 0 { // For jail to mount filesystem, enforce_statfs should be 1 or lower (2 is the default) - // TODO : Write these changes in jail config file - jail.Config.Allow_mount = 1 - jail.Config.Allow_mount_zfs = 1 + if jail.Config.Allow_mount != 1 { + jail.Config.Allow_mount = 1 + setJailConfigUpdated(jail) + } + if jail.Config.Allow_mount_zfs != 1 { + jail.Config.Allow_mount_zfs = 1 + setJailConfigUpdated(jail) + } // TODO : Overload Json Unmarshalling to fix bad typed values, keeping iocage compatibility if jail.Config.Enforce_statfs > "1" { jail.Config.Enforce_statfs = "1" + setJailConfigUpdated(jail) } for _, d := range strings.Split(jail.Config.Jail_zfs_dataset, " ") { // Check if dataset exist, create if necessary @@ -450,14 +452,7 @@ func genNatIpv4(jail *Jail) ([]string, error) { func buildDevfsRuleSet(jail *Jail) (error, int) { rulesets := []int{} - // TODO : Could be replaced by "add include $devfsrules_unhide_login" (see /etc/devfs.rules) - /*default_devs := [47]string {"hide", "null", "zero", "crypto", "random", "urandom", "ptyp*", - "ptyq*", "ptyr*", "ptys*", "ptyP*", "ptyQ*", "ptyR*", "ptyS*", "ptyl*", - "ptym*", "ptyn*", "ptyo*", "ptyL*", "ptyM*", "ptyN*", "pty0*", "ttyp*", - "ttyq*", "ttyr*", "ttys*", "ttyP*", "ttyQ*", "ttyR*", "ttyS*", "ttyl*", - "ttym*", "ttyn*", "ttyo*", "ttyL*", "ttyM*", "ttyN*", "tty0*", "ptmx", - "pts", "pts/*", "fd", "fd/*", "stdin", "stdout", "stderr", "zfs"} - */ + // Get known rulesets out, err := executeCommand("devfs rule showsets") if err != nil { @@ -480,22 +475,25 @@ func buildDevfsRuleSet(jail *Jail) (error, int) { } } + log.Debug("buildDevfsRuleSet: Build ruleset %d\n", ruleset) + // Get default devfs_ruleset for the datastore - ds, err := getDatastoreFromArray(jail.Datastore, gDatastores) + // UPDATE: We don't need this as every jail have a default Devfs_ruleset value + /*ds, err := getDatastoreFromArray(jail.Datastore, gDatastores) if err != nil { return errors.New(fmt.Sprintf("Error getting datastore %s for jail %s", jail.Datastore, jail.Name)), 0 } defaultrs, err := strconv.ParseInt(ds.DefaultJailConfig.Devfs_ruleset, 10, 64) if err != nil { return errors.New(fmt.Sprintf("Error parsing default devfs_ruleset for datastore %s", jail.Datastore)), 0 - } + }*/ - // Clone configured devfs_rulesetto a dynamic ruleset + // Clone configured devfs_ruleset to a dynamic ruleset if false == isStringInArray(srs, jail.Config.Devfs_ruleset) { return errors.New(fmt.Sprintf("Unknown ruleset: %s", jail.Config.Devfs_ruleset)), 0 } rs, _ := strconv.Atoi(jail.Config.Devfs_ruleset) - err := copyDevfsRuleset(ruleset, rs) + err = copyDevfsRuleset(ruleset, rs) if err != nil { return err, 0 } @@ -523,6 +521,204 @@ func buildDevfsRuleSet(jail *Jail) (error, int) { return nil, ruleset } +/***************************************************************************** + * Build the file containing jail start parameters, in + * /var/run/jail.${jail_internal_name}.conf + ****************************************************************************/ +func buildJailParameters(jail *Jail, dynruleset int) error { + parameterFilePath := fmt.Sprintf("/var/run/jail.%s.conf", jail.InternalName) + + pfile, err := os.Create(parameterFilePath) + if err != nil { + return errors.New(fmt.Sprintf("Unable to create file /var/run/jail.%d.conf", jail.InternalName)) + } + defer pfile.Close() + + if _, err = pfile.WriteString(fmt.Sprintf("%s {\n", jail.InternalName)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + + if jail.Config.Vnet > 0 { + if _, err = pfile.WriteString(" vnet;\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + // FreeBSD 9.3 and under do not support these 2 options + if gJailHost.version.major > 9 || (gJailHost.version.major == 9 && gJailHost.version.minor > 3) { + if _, err = pfile.WriteString(" mount.fdescfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if jail.Config.Allow_mount_tmpfs > 0 { + if _, err = pfile.WriteString(" allow.mount.tmpfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + } + + if gJailHost.version.major >= 12 { + if jail.Config.Allow_mlock > 0 { + if _, err = pfile.WriteString(" allow.mlock = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_mount_fusefs > 0 { + if _, err = pfile.WriteString(" allow.mount.fusefs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_vmm > 0 { + if _, err = pfile.WriteString(" allow.vmm = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + } + + if jail.Config.Allow_set_hostname > 0 { + if _, err = pfile.WriteString(" allow.set_hostname = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Mount_devfs > 0 { + if _, err = pfile.WriteString(" mount.devfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_raw_sockets > 0 { + if _, err = pfile.WriteString(" allow.raw_sockets = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_sysvipc > 0 { + if _, err = pfile.WriteString(" allow.sysvipc = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_quotas > 0 { + if _, err = pfile.WriteString(" allow.quotas = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_socket_af > 0 { + if _, err = pfile.WriteString(" allow.socket_af = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_chflags > 0 { + if _, err = pfile.WriteString(" allow.chflags = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_mount > 0 { + if _, err = pfile.WriteString(" allow.mount = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_mount_devfs > 0 { + if _, err = pfile.WriteString(" allow.mount.devfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_mount_nullfs > 0 { + if _, err = pfile.WriteString(" allow.mount.nullfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_mount_procfs > 0 { + if _, err = pfile.WriteString(" allow.mount.procfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if jail.Config.Allow_mount_zfs > 0 { + if _, err = pfile.WriteString(" allow.mount.zfs = \"1\";\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if gJailHost.version.major > 10 || (gJailHost.version.major == 10 && gJailHost.version.minor > 3) { + if _, err = pfile.WriteString(fmt.Sprintf(" sysvmsg = \"%s\";\n", jail.Config.Sysvmsg)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" sysvsem = \"%s\";\n", jail.Config.Sysvsem)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" sysvshm = \"%s\";\n", jail.Config.Sysvshm)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if gJailHost.version.major >= 12 { + if _, err = pfile.WriteString(fmt.Sprintf(" exec.created = \"%s\";\n", jail.Config.Exec_created)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if _, err = pfile.WriteString(fmt.Sprintf(" host.domainname = \"%s\";\n", jail.Config.Host_domainname)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" host.hostname = \"%s\";\n", jail.Config.Host_hostname)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" path = \"%s\";\n", jail.RootPath)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" securelevel = \"%s\";\n", jail.Config.Securelevel)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + + // FIXME : Handle the same name jail on different datastores + if _, err = pfile.WriteString(fmt.Sprintf(" host.hostuuid = \"%s\";\n", jail.Config.Host_hostuuid)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + + if _, err = pfile.WriteString(fmt.Sprintf(" devfs_ruleset = \"%d\";\n", dynruleset)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" enforce_statfs = \"%s\";\n", jail.Config.Enforce_statfs)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" children.max = \"%s\";\n", jail.Config.Children_max)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" exec.clean = \"%d\";\n", jail.Config.Exec_clean)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" exec.timeout = \"%s\";\n", jail.Config.Exec_timeout)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(fmt.Sprintf(" stop.timeout = \"%s\";\n", jail.Config.Stop_timeout)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + splits := strings.Split(jail.RootPath, "/") + fstabPath := strings.Join(append(splits[:len(splits)-1], + strings.Replace(splits[len(splits)-1], "root", "fstab", 1)), "/") + if _, err = pfile.WriteString(fmt.Sprintf(" mount.fstab = \"%s\";\n", fstabPath)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if _, err = pfile.WriteString(" allow.dying;\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + // get current datastore to get mountpoint + ds, err := getDatastoreFromArray(jail.Datastore, gDatastores) + if err != nil { + return errors.New(fmt.Sprintf("Unable to get datastore %s for jail %s", jail.Datastore, jail.Name)) + } + consolelog := fmt.Sprintf("%s/log/%s-console.log", ds.Mountpoint, jail.InternalName) + if _, err = pfile.WriteString(fmt.Sprintf(" exec.consolelog = \"%s\";\n", consolelog)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + if jail.Config.Ip_hostname > 0 { + if _, err = pfile.WriteString(fmt.Sprintf(" ip_hostname = \"%s\";\n", jail.Config.Ip_hostname)); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + } + if _, err = pfile.WriteString(" persist;\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + + if _, err = pfile.WriteString("}\n"); err != nil { + return errors.New(fmt.Sprintf("Unable to write to file /var/run/jail.%d.conf: %v", jail.InternalName, err)) + } + return nil +} + + /* Start jail: Check jail fstab? @@ -574,13 +770,7 @@ func StartJail(args []string) { } if len(cj.Config.Hostid) > 0 && cj.Config.Hostid_strict_check > 0 { - hostid, err := ioutil.ReadFile("/etc/hostid") - if err != nil { - fmt.Printf(err.Error()) - return - } - hostid = []byte(strings.Replace(string(hostid), "\n", "", -1)) - if strings.EqualFold(string(hostid), cj.Config.Hostid) == false { + if strings.EqualFold(gJailHost.hostid, cj.Config.Hostid) == false { fmt.Printf("hostid is not matching and hostid_strict_check is on. Not starting jail.\n") return } @@ -721,24 +911,24 @@ func StartJail(args []string) { } if cj.Config.Vnet == 0 { - log.Debug("Generate NAT IPv4 without VNet") + log.Debug("Generate NAT IPv4 without VNet\n") ip4, err := genNatIpv4(cj) if err != nil { fmt.Printf("%s\n", err.Error()) return } - log.Debug("Configuring NAT : Set ip4_addr to %s", ip4[0]) + log.Debug("Configuring NAT : Set ip4_addr to %s\n", ip4[0]) // This IP should not be saved into json cj.Config.Ip4_addr = fmt.Sprintf("%s|%s", cj.Config.Nat_interface, ip4[0]) } else { - log.Debug("Generate NAT IPv4 with VNet") + log.Debug("Generate NAT IPv4 with VNet\n") ip4, err := genNatIpv4(cj) if err != nil { fmt.Printf("%s\n", err.Error()) return } - log.Debug("Configuring NAT : Set ip4_addr to %s, defaultrouter to %s", ip4[1], ip4[0]) + log.Debug("Configuring NAT : Set ip4_addr to %s, defaultrouter to %s\n", ip4[1], ip4[0]) // This IP should not be saved into json cj.Config.Ip4_addr = fmt.Sprintf("vnet0|%s", ip4[1]) cj.Config.Defaultrouter = ip4[0] @@ -757,14 +947,21 @@ func StartJail(args []string) { net = append(net, strings.Split(cj.Config.Vnet_interfaces, " ")...) } - err, rs := buildDevfsRuleSet(cj) + err, dynrs := buildDevfsRuleSet(cj) if err != nil { fmt.Printf("%s\n", err.Error()) return } - // CONTINUE HERE, around https://github.com/iocage/iocage/blob/master/iocage_lib/ioc_start.py:516 - fmt.Printf("Built ruleset: %d\n", rs) + // CONTINUE HERE, around https://github.com/iocage/iocage/blob/master/iocage_lib/ioc_start.py:516 + err = buildJailParameters(cj, dynrs) + if err != nil { + fmt.Printf("%s\n", err.Error()) + return + } + + // Synchronize jail config to disk + WriteConfigToDisk(false) /* out, err := executeCommand(fmt.Sprintf("rctl jail:%s", cj.InternalName))