// generateDeviceAllows generates a DeviceAllow= line for an app. // To make it work, the path needs to start with "/dev" but the device won't // exist inside the container. So for a given mount, if the volume is a device // node, we create a symlink to its target in "/rkt/volumes". Later, // prepare-app will copy those to "/dev/.rkt/" so that's what we use in the // DeviceAllow= line. func generateDeviceAllows(root string, appName types.ACName, mountPoints []types.MountPoint, mounts []mountWrapper, vols map[types.ACName]types.Volume, uidRange *user.UidRange) ([]string, error) { var devAllow []string rktVolumeLinksPath := filepath.Join(root, "rkt", "volumes") if err := os.MkdirAll(rktVolumeLinksPath, 0600); err != nil { return nil, err } if err := user.ShiftFiles([]string{rktVolumeLinksPath}, uidRange); err != nil { return nil, err } for _, m := range mounts { v := vols[m.Volume] if v.Kind != "host" { continue } if fileutil.IsDeviceNode(v.Source) { mode := "r" if !IsMountReadOnly(v, mountPoints) { mode += "w" } tgt := filepath.Join(common.RelAppRootfsPath(appName), m.Path) // the DeviceAllow= line needs the link path in /dev/.rkt/ linkRel := filepath.Join("/dev/.rkt", v.Name.String()) // the real link should be in /rkt/volumes for now link := filepath.Join(rktVolumeLinksPath, v.Name.String()) err := os.Symlink(tgt, link) // if the link already exists, we don't need to do anything if err != nil && !os.IsExist(err) { return nil, err } devAllow = append(devAllow, linkRel+" "+mode) } } return devAllow, nil }
// WriteEnvFile creates an environment file for given app name, the minimum // required environment variables by the appc spec will be set to sensible // defaults here if they're not provided by env. func WriteEnvFile(env types.Environment, uidRange *user.UidRange, envFilePath string) error { ef := bytes.Buffer{} for dk, dv := range defaultEnv { if _, exists := env.Get(dk); !exists { fmt.Fprintf(&ef, "%s=%s\n", dk, dv) } } for _, e := range env { fmt.Fprintf(&ef, "%s=%s\n", e.Name, e.Value) } if err := ioutil.WriteFile(envFilePath, ef.Bytes(), 0644); err != nil { return err } if err := user.ShiftFiles([]string{envFilePath}, uidRange); err != nil { return err } return nil }
// generateSysusers generates systemd sysusers files for a given app so that // corresponding entries in /etc/passwd and /etc/group are created in stage1. // This is needed to use the "User="******"Group=" options in the systemd // service files of apps. // If there're several apps defining the same UIDs/GIDs, systemd will take care // of only generating one /etc/{passwd,group} entry func generateSysusers(p *stage1commontypes.Pod, ra *schema.RuntimeApp, uid_ int, gid_ int, uidRange *user.UidRange) error { var toShift []string app := ra.App appName := ra.Name sysusersDir := path.Join(common.Stage1RootfsPath(p.Root), "usr/lib/sysusers.d") toShift = append(toShift, sysusersDir) if err := os.MkdirAll(sysusersDir, 0755); err != nil { return err } gids := append(app.SupplementaryGIDs, gid_) // Create the Unix user and group var sysusersConf []string for _, g := range gids { groupname := "gen" + strconv.Itoa(g) sysusersConf = append(sysusersConf, fmt.Sprintf("g %s %d\n", groupname, g)) } username := "******" + strconv.Itoa(uid_) sysusersConf = append(sysusersConf, fmt.Sprintf("u %s %d \"%s\"\n", username, uid_, username)) sysusersFile := path.Join(common.Stage1RootfsPath(p.Root), "usr/lib/sysusers.d", ServiceUnitName(appName)+".conf") toShift = append(toShift, sysusersFile) if err := ioutil.WriteFile(sysusersFile, []byte(strings.Join(sysusersConf, "\n")), 0640); err != nil { return err } if err := user.ShiftFiles(toShift, uidRange); err != nil { return err } return nil }