on master help sign in
Merge pull request #26 from input-output-hk/overhaul

overhaul most of nix-bundle

commit0c91c1282b29a1ca992534fa9cf8bfa659a99329 authorMatthew Justin Bauer <mjbauer95@gmail.com> dateSat 3 Mar 2018, 14:55 (9 years ago) parentffa3349630e89d
3 files changed+252−10
default.nix+13−10 nix-user-chroot/Makefile+6 nix-user-chroot/main.cpp+233
default.nix+13−10
@@ -30,12 +30,7 @@ rec {
3030 # TODO: eventually should this go in nixpkgs?
3131 nix-user-chroot = stdenv.lib.makeOverridable stdenv.mkDerivation {
3232 name = "nix-user-chroot-2c52b5f";
33− src = fetchFromGitHub {
34− owner = "matthewbauer";
35− repo = "nix-user-chroot";
36− rev = "2c52b5f3174e382c2bfdd9e61f3e4a1200077b93";
37− sha256 = "139ixrg5ihrgsmi2nl1ws3xmi0vqwl5nwfijrggg02wlbiqvdiwq";
38− };
33+ src = ./nix-user-chroot;
3934
4035 # hack to use when /nix/store is not available
4136 postFixup = ''
@@ -64,10 +59,18 @@ rec {
6459 };
6560 };
6661
67− nix-bootstrap = { target, extraTargets ? [], run, nix-user-chroot' ? nix-user-chroot }:
68− makebootstrap {
69− startup = ''.${nix-user-chroot'}/bin/nix-user-chroot ./nix ${target}${run} \$@'';
70− targets = [ nix-user-chroot' target ] ++ extraTargets;
62+ makeStartup = { target, nixUserChrootFlags, nix-user-chroot', run }:
63+ writeScript "startup" ''
64+ #!/bin/sh
65+ .${nix-user-chroot'}/bin/nix-user-chroot -n ./nix ${nixUserChrootFlags} -- ${target}${run} $@
66+ '';
67+
68+ nix-bootstrap = { target, extraTargets ? [], run, nix-user-chroot' ? nix-user-chroot, nixUserChrootFlags ? "" }:
69+ let
70+ script = makeStartup { inherit target nixUserChrootFlags nix-user-chroot' run; };
71+ in makebootstrap {
72+ startup = ".${script}";
73+ targets = [ "${script}" ] ++ extraTargets;
7174 };
7275
7376 # special case handling because of impurities in nix bootstrap
nix-user-chroot/Makefilenew file+6
@@ -0,0 +1,6 @@
1+NIX_PATH ?= "nixpkgs=https://github.com/NixOS/nixpkgs/archive/master.tar.gz"
2+NIX_SSL_CERT_FILE ?= "/homeless-shelter"
3+ENV_PATH ?= ""
4+
5+nix-user-chroot: main.cpp
6+ g++ -o nix-user-chroot -DNIX_PATH='$(NIX_PATH)' -DNIX_SSL_CERT_FILE='$(NIX_SSL_CERT_FILE)' -DENV_HOME='$(ENV_HOME)' -DENV_PATH='$(ENV_PATH)' main.cpp
nix-user-chroot/main.cppnew file+233
@@ -0,0 +1,233 @@
1+/*
2+ * This file is based on @lethalman's nix-user-chroot. This file has
3+ * diverged from it though.
4+ *
5+ * Usage: nix-user-chroot <nixpath> <command>
6+ */
7+
8+#include <sched.h>
9+#include <unistd.h>
10+#include <stdlib.h>
11+#include <sys/wait.h>
12+#include <signal.h>
13+#include <fcntl.h>
14+#include <stdio.h>
15+#include <string.h>
16+#include <limits.h>
17+#include <errno.h>
18+#include <sys/mount.h>
19+#include <sys/types.h>
20+#include <dirent.h>
21+#include <sys/stat.h>
22+#include <stdint.h>
23+#include <list>
24+#include <string>
25+
26+using namespace std;
27+
28+#define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); }
29+
30+static void usage(const char *pname) {
31+ fprintf(stderr, "Usage: %s -n <nixpath> -- <command>\n", pname);
32+ fprintf(stderr, "\t-c\tclear all env vars\n");
33+ fprintf(stderr, "\t-m <src>:<src>\tmap src on the host to dest in the sandbox\n");
34+ fprintf(stderr, "\t-d\tdelete all default dir mappings, may break things\n");
35+ fprintf(stderr, "\t-p <var>\tpreserve the value of a variable across the -c clear\n");
36+
37+ exit(EXIT_FAILURE);
38+}
39+
40+static void update_map(const char *mapping, const char *map_file) {
41+ int fd;
42+
43+ fd = open(map_file, O_WRONLY);
44+ if (fd < 0) {
45+ err_exit("map open");
46+ }
47+
48+ int map_len = strlen(mapping);
49+ if (write(fd, mapping, map_len) != map_len) {
50+ err_exit("map write");
51+ }
52+
53+ close(fd);
54+}
55+
56+static void add_path(string src, string dest, string rootdir) {
57+ string path_buf2;
58+
59+ struct stat statbuf;
60+ if (stat(src.c_str(), &statbuf) < 0) {
61+ fprintf(stderr, "Cannot stat %s: %s\n", src.c_str(), strerror(errno));
62+ return;
63+ }
64+
65+ path_buf2 = rootdir + "/" + dest;
66+
67+ if (S_ISDIR(statbuf.st_mode)) {
68+ mkdir(path_buf2.c_str(), statbuf.st_mode & ~S_IFMT);
69+ if (mount(src.c_str(), path_buf2.c_str(), "none", MS_BIND | MS_REC, NULL) < 0) {
70+ fprintf(stderr, "Cannot bind mount %s to %s: %s\n", src.c_str(), path_buf2.c_str(), strerror(errno));
71+ }
72+ } else if (S_ISREG(statbuf.st_mode)) {
73+ printf("bind-mounting file %s not supported", src.c_str());
74+ }
75+}
76+
77+struct DirMapping {
78+ string src;
79+ string dest;
80+};
81+
82+struct SetEnv {
83+ string key;
84+ string value;
85+};
86+
87+struct DirMapping parseMapping(string input) {
88+ auto pos = input.find(":");
89+ string src = input.substr(0, pos);
90+ string dest = input.substr(pos + 1);
91+ return (struct DirMapping){ src, dest };
92+}
93+
94+int main(int argc, char *argv[]) {
95+ uint8_t clear_env = 0;
96+ char *nixdir = NULL;
97+ list<struct DirMapping> dirMappings;
98+ list<struct SetEnv> envMappings;
99+ const char *t;
100+
101+#define x(y) dirMappings.push_back({ "/" y, y })
102+ x("dev");
103+ x("proc");
104+ x("sys");
105+ x("run");
106+ x("tmp");
107+ x("var");
108+#undef x
109+
110+ int opt;
111+ while ((opt = getopt(argc, argv, "cn:m:dp:")) != -1) {
112+ switch (opt) {
113+ case 'c':
114+ clear_env = 1;
115+ puts("clearing env");
116+ break;
117+ case 'n':
118+ // determine absolute directory for nix dir
119+ nixdir = realpath(optarg, NULL);
120+ if (!nixdir) {
121+ err_exit("realpath(%s)", optarg);
122+ }
123+ break;
124+ case 'm':
125+ dirMappings.push_back(parseMapping(optarg));
126+ break;
127+ case 'd':
128+ dirMappings.clear();
129+ break;
130+ case 'p':
131+ t = getenv(optarg);
132+ if (t) {
133+ envMappings.push_back({ optarg, t });
134+ }
135+ break;
136+ }
137+ }
138+
139+ if (!nixdir) {
140+ fprintf(stderr, "-n <nixdir> is required\n");
141+ exit(EXIT_FAILURE);
142+ }
143+
144+ if (argc <= optind) {
145+ usage(argv[0]);
146+ }
147+
148+ const char *tmpdir = getenv("TMPDIR");
149+ if (!tmpdir) {
150+ tmpdir = "/tmp";
151+ }
152+
153+ char template_[PATH_MAX];
154+ int needed = snprintf(template_, PATH_MAX, "%s/nixXXXXXX", tmpdir);
155+ if (needed < 0) {
156+ err_exit("TMPDIR too long: '%s'", tmpdir);
157+ }
158+
159+ char *rootdir = mkdtemp(template_);
160+ if (!rootdir) {
161+ err_exit("mkdtemp(%s)", template_);
162+ }
163+
164+ // get uid, gid before going to new namespace
165+ uid_t uid = getuid();
166+ gid_t gid = getgid();
167+
168+ // "unshare" into new namespace
169+ if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) {
170+ err_exit("unshare()");
171+ }
172+
173+ // add necessary system stuff to rootdir namespace
174+ for (list<struct DirMapping>::iterator it = dirMappings.begin();
175+ it != dirMappings.end(); ++it) {
176+ struct DirMapping m = *it;
177+ add_path(m.src, m.dest, rootdir);
178+ }
179+
180+ char path_buf[PATH_MAX];
181+ snprintf(path_buf, sizeof(path_buf), "%s/tmp", rootdir);
182+ mkdir(path_buf, ~0);
183+ snprintf(path_buf, sizeof(path_buf), "%s/var", rootdir);
184+
185+ // make sure nixdir exists
186+ struct stat statbuf2;
187+ if (stat(nixdir, &statbuf2) < 0) {
188+ err_exit("stat(%s)", nixdir);
189+ }
190+
191+ // mount /nix to new namespace
192+ snprintf(path_buf, sizeof(path_buf), "%s/nix", rootdir);
193+ mkdir(path_buf, statbuf2.st_mode & ~S_IFMT);
194+ if (mount(nixdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) {
195+ err_exit("mount(%s, %s)", nixdir, path_buf);
196+ }
197+
198+ // fixes issue #1 where writing to /proc/self/gid_map fails
199+ // see user_namespaces(7) for more documentation
200+ int fd_setgroups = open("/proc/self/setgroups", O_WRONLY);
201+ if (fd_setgroups > 0) {
202+ write(fd_setgroups, "deny", 4);
203+ }
204+
205+ // map the original uid/gid in the new ns
206+ char map_buf[1024];
207+ snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid);
208+ update_map(map_buf, "/proc/self/uid_map");
209+ snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid);
210+ update_map(map_buf, "/proc/self/gid_map");
211+
212+ // chroot to rootdir
213+ if (chroot(rootdir) < 0) {
214+ err_exit("chroot(%s)", rootdir);
215+ }
216+
217+ chdir("/");
218+
219+ if (clear_env) clearenv();
220+ setenv("NIX_PATH", NIX_PATH, 1);
221+ setenv("NIX_SSL_CERT_FILE", NIX_SSL_CERT_FILE, 1);
222+ setenv("PATH", ENV_PATH, 1);
223+
224+ for (list<struct SetEnv>::iterator it = envMappings.begin();
225+ it != envMappings.end(); ++it) {
226+ struct SetEnv e = *it;
227+ setenv(e.key.c_str(), e.value.c_str(), 1);
228+ }
229+
230+ // execute the command
231+ execvp(argv[optind], argv + optind);
232+ err_exit("execvp(%s)", argv[optind]);
233+}