From 6dc15dff24984bb52c658b68955dd037de4523fc Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Fri, 18 Nov 2016 10:53:30 -0600 Subject: [PATCH] Revert "Use proot instead of nix-user-chroot." This reverts commit f24309c9d1e74f3266debcea380ea76a7b1b72c5. --- default.nix | 4 +- nix-bootstrap.nix | 6 +- nix-user-chroot.c | 134 ++++++++++++++++++++++++++++++++++++++++++++ nix-user-chroot.nix | 14 +++++ 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 nix-user-chroot.c create mode 100644 nix-user-chroot.nix diff --git a/default.nix b/default.nix index 7e4af5a..8784396 100644 --- a/default.nix +++ b/default.nix @@ -16,8 +16,10 @@ let inherit arx maketar; }; + nix-user-chroot = callPackage ./nix-user-chroot.nix {}; + nix-bootstrap = callPackage ./nix-bootstrap.nix { - inherit makebootstrap; + inherit nix-user-chroot makebootstrap; }; in { diff --git a/nix-bootstrap.nix b/nix-bootstrap.nix index a2c36dc..e4d2c78 100644 --- a/nix-bootstrap.nix +++ b/nix-bootstrap.nix @@ -1,9 +1,9 @@ -{ stdenv, writeText, proot, makebootstrap }: +{ stdenv, writeText, nix-user-chroot, makebootstrap }: { name, target, run }: makebootstrap { inherit name; - startup = ".${proot}/bin/proot -b./nix:/nix ${target}${run}"; - targets = [ proot target ]; + startup = ".${nix-user-chroot}/bin/nix-user-chroot ./nix ${target}${run}"; + targets = [ nix-user-chroot target ]; } diff --git a/nix-user-chroot.c b/nix-user-chroot.c new file mode 100644 index 0000000..ca218a2 --- /dev/null +++ b/nix-user-chroot.c @@ -0,0 +1,134 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); } + +static void usage(char *pname) { + fprintf(stderr, "Usage: %s \n", pname); + + exit(EXIT_FAILURE); +} + +static void update_map(char *mapping, char *map_file) { + int fd; + + fd = open(map_file, O_WRONLY); + if (fd < 0) { + err_exit("map open"); + } + + int map_len = strlen(mapping); + if (write(fd, mapping, map_len) != map_len) { + err_exit("map write"); + } + + close(fd); +} + +static void add_path(const char* name, const char* rootdir) { + char path_buf[PATH_MAX]; + char path_buf2[PATH_MAX]; + + snprintf(path_buf, sizeof(path_buf), "/%s", name); + + struct stat statbuf; + if (stat(path_buf, &statbuf) < 0) { + fprintf(stderr, "Cannot stat %s: %s\n", path_buf, strerror(errno)); + return; + } + + snprintf(path_buf2, sizeof(path_buf2), "%s/%s", rootdir, name); + + if (S_ISDIR(statbuf.st_mode)) { + mkdir(path_buf2, statbuf.st_mode & ~S_IFMT); + if (mount(path_buf, path_buf2, "none", MS_BIND | MS_REC, NULL) < 0) { + fprintf(stderr, "Cannot bind mount %s to %s: %s\n", path_buf, path_buf2, strerror(errno)); + } + } +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + usage(argv[0]); + } + + // Create skeleton root dir in temp. All dirs will be mounted here. + char template[] = "/tmp/nixXXXXXX"; + char *rootdir = mkdtemp(template); + if (!rootdir) { + err_exit("mkdtemp(%s)", template); + } + + // determine absolute directory for nix dir + char *nixdir = realpath(argv[1], NULL); + if (!nixdir) { + err_exit("realpath(%s)", argv[1]); + } + + // get uid, gid before going to new namespace + uid_t uid = getuid(); + gid_t gid = getgid(); + + // "unshare" into new namespace + if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) { + err_exit("unshare()"); + } + + // add necessary system stuff to rootdir namespace + add_path("dev", rootdir); + add_path("proc", rootdir); + add_path("sys", rootdir); + add_path("run", rootdir); + add_path("tmp", rootdir); + add_path("var", rootdir); + + // make sure nixdir exists + struct stat statbuf2; + if (stat(nixdir, &statbuf2) < 0) { + err_exit("stat(%s)", nixdir); + } + + // mount /nix to new namespace + char path_buf[PATH_MAX]; + snprintf(path_buf, sizeof(path_buf), "%s/nix", rootdir); + mkdir(path_buf, statbuf2.st_mode & ~S_IFMT); + if (mount(nixdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) { + err_exit("mount(%s, %s)", nixdir, path_buf); + } + + // fixes issue #1 where writing to /proc/self/gid_map fails + // see user_namespaces(7) for more documentation + int fd_setgroups = open("/proc/self/setgroups", O_WRONLY); + if (fd_setgroups > 0) { + write(fd_setgroups, "deny", 4); + } + + // map the original uid/gid in the new ns + char map_buf[1024]; + snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid); + update_map(map_buf, "/proc/self/uid_map"); + snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid); + update_map(map_buf, "/proc/self/gid_map"); + + // chroot to rootdir + if (chroot(rootdir) < 0) { + err_exit("chroot(%s)", rootdir); + } + + // execute the command + execvp(argv[2], argv+2); + err_exit("execvp(%s)", argv[2]); +} diff --git a/nix-user-chroot.nix b/nix-user-chroot.nix new file mode 100644 index 0000000..acec1bb --- /dev/null +++ b/nix-user-chroot.nix @@ -0,0 +1,14 @@ +{ stdenv, fetchFromGitHub }: + + stdenv.mkDerivation { + name = "nix-user-chroot"; + phases = [ "buildPhase" "installPhase" "fixupPhase" ]; + buildPhase = '' + cp ${./nix-user-chroot.c} nix-user-chroot.c + $CC nix-user-chroot.c -o nix-user-chroot + ''; + installPhase = '' + mkdir -p $out/bin/ + cp nix-user-chroot $out/bin/nix-user-chroot + ''; + }