diff --git a/Makefile b/Makefile index 3c75afe..c99ef08 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ image.tar: docker export $(shell docker create $(DOCKER_ROOTFS_IMAGE) sh) > $@ rootfs.go: image.tar - go generate + GOMAXPROCS=1 go generate fmt: @echo "+ $@" @@ -76,6 +76,7 @@ clean: clean-rootfs @$(RM) *.tar @$(RM) rootfs.go @$(RM) -r $(BINDIR) + -@docker rm $(shell docker ps -aq) /dev/null 2>&1 install: @echo "+ $@" diff --git a/README.md b/README.md index 7c9822c..1632494 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,19 @@ This is based off a crazy idea from [@crosbymichael](https://github.com/crosbymi **NOTE** You may have noticed you can't file an issue. That's because this is using a crazy -person's (aka my) fork of libcontainer -and until I get the patches into upstream there's no -way in hell I'm fielding issues from whoever is crazy -enough to try this. +person's (aka my) fork of libcontainer and until I get the patches into upstream +there's no way in hell I'm fielding issues from whoever is crazy enough to try this. ### Building +This uses the new Golang vendoring so you need go 1.6 or +`GO15VENDOREXPERIMENT=1` in your env. + +You will also need `libapparmor-dev` and `libseccomp-dev`. + +Most importantly you need userns in your kernel (`CONFIG_USER_NS=y`) +or else this won't even work. + ```console $ make static Static container created at: ./bin/alpine @@ -97,12 +103,18 @@ $ ./bin/alpine -h ## Cool things The binary spawned does NOT need to oversee the container process if you -run in detached mode with a PID file. You can have it watched by the user mode +run in detached mode with a PID file. You can have it watched by the user mode systemd so that this binary is really just the launcher :) +## Example + +Nginx running with my user "jessie". + +![nginx.png](nginx.png) + ## Caveats -**Caps the binary needs to unpack and set +**Caps the binary needs to unpack and set the right perms on the roofs for the userns user** - **CAP_CHOWN**: chown the rootfs to the userns user diff --git a/main.go b/main.go index cff1d7a..e1651d7 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( aaprofile "github.com/docker/docker/profiles/apparmor" "github.com/opencontainers/runc/libcontainer" "github.com/opencontainers/runc/libcontainer/apparmor" + "github.com/opencontainers/runc/libcontainer/user" "github.com/opencontainers/runtime-spec/specs-go" ) @@ -180,13 +181,21 @@ func main() { // install the default apparmor profile if apparmor.IsEnabled() { - if err := aaprofile.InstallDefault(defaultApparmorProfile); err != nil { - logrus.Warnf("AppArmor is enabled on the the system, but the profile (%s) could not be loaded", defaultApparmorProfile) + // check if we have the docker-default apparmor profile loaded + if err := aaprofile.IsLoaded(defaultApparmorProfile); err != nil { + logrus.Warnf("AppArmor enabled on system but the %s profile is not loaded. apparmor_parser needs root to load a profile so we can't do it for you.", defaultApparmorProfile) } else { spec.Process.ApparmorProfile = defaultApparmorProfile } } + // set the CgroupsPath as this user + user, err := user.CurrentUser() + if err != nil { + logrus.Fatal(err) + } + spec.Linux.CgroupsPath = sPtr(user.Name) + if err := unpackRootfs(spec); err != nil { logrus.Fatal(err) } diff --git a/nginx.png b/nginx.png new file mode 100644 index 0000000..6a58713 Binary files /dev/null and b/nginx.png differ diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go index 582e527..3dea8b8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/apply_raw.go @@ -111,11 +111,14 @@ func (m *Manager) Apply(pid int) (err error) { var c = m.Cgroups + logrus.Debugf("pre get cgroups data: %#v", c) d, err := getCgroupData(m.Cgroups, pid) if err != nil { return err } + logrus.Debugf("cgroups data: %#v, config: %#v", d, d.config) + if c.Paths != nil { paths := make(map[string]string) for name, path := range c.Paths { @@ -129,6 +132,7 @@ func (m *Manager) Apply(pid int) (err error) { paths[name] = path } m.Paths = paths + logrus.Debugf("cgroups apply paths: %#v", m.Paths) return cgroups.EnterPid(m.Paths, pid) } @@ -136,6 +140,7 @@ func (m *Manager) Apply(pid int) (err error) { defer m.mu.Unlock() paths := make(map[string]string) for _, sys := range subsystems { + logrus.Debugf("applying cgroups to subsystem %#v", sys) if err := sys.Apply(d); err != nil { return err } @@ -353,23 +358,25 @@ func writeFile(dir, file, data string) error { if dir == "" { return fmt.Errorf("no such directory for %s", file) } - // get the current user u, err := user.CurrentUser() if err != nil { return err } - if err := os.Chown(dir, u.Uid, u.Gid); err != nil { - return fmt.Errorf("failed to chown %s to %d:%d -> %v", dir, u.Uid, u.Gid, err) + if err := os.Lchown(dir, u.Uid, u.Gid); err != nil { + return fmt.Errorf("failed to chown to %d:%d -> %v", u.Uid, u.Gid, err) } + logrus.Debugf("chown dir %s to %d:%d", dir, u.Uid, u.Gid) - if err := os.Chown(filepath.Join(dir, "tasks"), u.Uid, u.Gid); err != nil { - return fmt.Errorf("failed to chown %s/tasks to %d:%d -> %v", dir, u.Uid, u.Gid, err) + if err := os.Lchown(filepath.Join(dir, file), u.Uid, u.Gid); err != nil { + return fmt.Errorf("failed to chown to %d:%d -> %v", u.Uid, u.Gid, err) } + logrus.Debugf("chown %s to %d:%d", filepath.Join(dir, file), u.Uid, u.Gid) if err := ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700); err != nil { logrus.Debugf("failed to write %v to %v: %v", data, file, err) + //return err } return nil } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go index f2e30d4..9270746 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/cpuset.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strconv" + "github.com/Sirupsen/logrus" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" @@ -27,6 +28,7 @@ func (s *CpusetGroup) Apply(d *cgroupData) error { if err != nil && !cgroups.IsNotFound(err) { return err } + logrus.Debugf("apply cpuset dir is %s", dir) return s.ApplyDir(dir, d.config, d.pid) } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go index 70aaddd..11a6434 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/container_linux.go @@ -171,6 +171,7 @@ func (c *linuxContainer) Set(config configs.Config) error { c.m.Lock() defer c.m.Unlock() c.config = &config + logrus.Debugf("setting cgroups") return c.cgroupManager.Set(c.config) } @@ -741,6 +742,7 @@ func (c *linuxContainer) Restore(process *Process, criuOpts *CriuOpts) error { } func (c *linuxContainer) criuApplyCgroups(pid int, req *criurpc.CriuReq) error { + logrus.Debugf("criu apply cgroups") if err := c.cgroupManager.Apply(pid); err != nil { return err } diff --git a/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go b/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go index 1a2ee0b..f20fb08 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/process_linux.go @@ -13,6 +13,7 @@ import ( "strconv" "syscall" + "github.com/Sirupsen/logrus" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/system" @@ -247,6 +248,9 @@ func (p *initProcess) start() error { return newSystemError(err) } p.setExternalDescriptors(fds) + + logrus.Debugf("starting process apply cgroups") + // Do this before syncing with child so that no children // can escape the cgroup if err := p.manager.Apply(p.pid()); err != nil {