From 4afbd7947edede838ba72987c64dce4e339b8199 Mon Sep 17 00:00:00 2001 From: Luca Bruno Date: Wed, 24 Jun 2015 18:07:09 +0200 Subject: [PATCH] Initial commit --- Makefile | 3 ++ README.md | 6 +++ main.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 main.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..816ab61 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +nix-user-chroot: main.c + gcc -o nix-user-chroot main.c + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e8aa14 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +Run nix as user in a lightweight chrooted container. + +1. Create an empty directory somewhere, `$DIR` +2. Run `nix-user-chroot $DIR bash` + +You can now install nix under /nix as user. diff --git a/main.c b/main.c new file mode 100644 index 0000000..8e6bfff --- /dev/null +++ b/main.c @@ -0,0 +1,119 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define err_exit(msg) { perror(msg); 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); +} + +int main(int argc, char *argv[]) { + char map_buf[1024]; + char path_buf[PATH_MAX]; + char path_buf2[PATH_MAX]; + char cwd[PATH_MAX]; + + if (argc < 3) { + usage(argv[0]); + } + + char *rootdir = realpath(argv[1], NULL); + if (!rootdir) { + err_exit("realpath"); + } + + uid_t uid = getuid(); + gid_t gid = getgid(); + + if (unshare (CLONE_NEWNS | CLONE_NEWUSER) < 0) { + err_exit("unshare"); + } + + // bind mount all / stuff into rootdir + DIR* d = opendir("/"); + if (!d) { + err_exit("open /"); + } + + struct dirent *ent; + while ((ent = readdir(d))) { + // do not bind mount an existing nix installation + if (!strcmp (ent->d_name, ".") || !strcmp (ent->d_name, "..") || +!strcmp (ent->d_name, "nix")) { + continue; + } + + snprintf(path_buf, sizeof(path_buf), "/%s", ent->d_name); + + struct stat statbuf; + if (lstat(path_buf, &statbuf) < 0) { + fprintf(stderr, "Cannot stat %s: %s\n", path_buf, +strerror(errno)); + continue; + } + + snprintf(path_buf2, sizeof(path_buf2), "%s/%s", rootdir, +ent->d_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)); + } + } + } + + // map the original uid/gid in the new ns + 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"); + + if (!getcwd(cwd, PATH_MAX)) { + err_exit("getcwd"); + } + + chdir("/"); + if (chroot (rootdir) < 0) { + err_exit("chroot"); + } + chdir(cwd); + + // execute the command + execvp(argv[2], argv+2); + err_exit("execvp"); +}