runshare 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +38 -0
- data/LICENSE.txt +21 -0
- data/README.md +103 -0
- data/Rakefile +14 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/runshare/extconf.rb +33 -0
- data/ext/runshare/include/all-io.h +115 -0
- data/ext/runshare/include/c.h +479 -0
- data/ext/runshare/include/caputils.h +34 -0
- data/ext/runshare/include/namespace.h +56 -0
- data/ext/runshare/include/pathnames.h +218 -0
- data/ext/runshare/include/pwdutils.h +14 -0
- data/ext/runshare/include/signames.h +8 -0
- data/ext/runshare/include/strutils.h +393 -0
- data/ext/runshare/runshare.c +261 -0
- data/ext/runshare/unshare.c +498 -0
- data/ext/runshare/unshare.h +82 -0
- data/lib/runshare/version.rb +3 -0
- data/lib/runshare.rb +7 -0
- data/runshare.gemspec +44 -0
- metadata +231 -0
@@ -0,0 +1,498 @@
|
|
1
|
+
/*
|
2
|
+
* unshare(1) - command-line interface for unshare(2)
|
3
|
+
*
|
4
|
+
* Copyright (C) 2009 Mikhail Gusarov <dottedmag@dottedmag.net>
|
5
|
+
*
|
6
|
+
* This program is free software; you can redistribute it and/or modify it
|
7
|
+
* under the terms of the GNU General Public License as published by the
|
8
|
+
* Free Software Foundation; either version 2, or (at your option) any
|
9
|
+
* later version.
|
10
|
+
*
|
11
|
+
* This program is distributed in the hope that it will be useful, but
|
12
|
+
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14
|
+
* General Public License for more details.
|
15
|
+
*
|
16
|
+
* You should have received a copy of the GNU General Public License along
|
17
|
+
* with this program; if not, write to the Free Software Foundation, Inc.,
|
18
|
+
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
19
|
+
*/
|
20
|
+
|
21
|
+
#include <errno.h>
|
22
|
+
#include <grp.h>
|
23
|
+
#include <ruby.h>
|
24
|
+
#include <sched.h>
|
25
|
+
#include <stdlib.h>
|
26
|
+
#include <sys/mount.h>
|
27
|
+
#include <sys/prctl.h>
|
28
|
+
#include <sys/stat.h>
|
29
|
+
#include <sys/types.h>
|
30
|
+
#include <sys/wait.h>
|
31
|
+
#include <unistd.h>
|
32
|
+
|
33
|
+
#include "include/c.h"
|
34
|
+
#include "include/caputils.h"
|
35
|
+
#include "include/namespace.h"
|
36
|
+
#include "include/pathnames.h"
|
37
|
+
#include "include/all-io.h"
|
38
|
+
|
39
|
+
#include "unshare.h"
|
40
|
+
|
41
|
+
/* /proc namespace files and mountpoints for binds */
|
42
|
+
static struct namespace_file {
|
43
|
+
int type; /* CLONE_NEW* */
|
44
|
+
const char *name; /* ns/<type> */
|
45
|
+
const char *target; /* user specified target for bind mount */
|
46
|
+
} namespace_files[] = {
|
47
|
+
{ .type = CLONE_NEWUSER, .name = "ns/user" },
|
48
|
+
{ .type = CLONE_NEWCGROUP,.name = "ns/cgroup" },
|
49
|
+
{ .type = CLONE_NEWIPC, .name = "ns/ipc" },
|
50
|
+
{ .type = CLONE_NEWUTS, .name = "ns/uts" },
|
51
|
+
{ .type = CLONE_NEWNET, .name = "ns/net" },
|
52
|
+
{ .type = CLONE_NEWPID, .name = "ns/pid_for_children" },
|
53
|
+
{ .type = CLONE_NEWNS, .name = "ns/mnt" },
|
54
|
+
{ .type = CLONE_NEWTIME, .name = "ns/time_for_children" },
|
55
|
+
{ .name = NULL }
|
56
|
+
};
|
57
|
+
|
58
|
+
static int npersists = 0; /* number of persistent namespaces */
|
59
|
+
|
60
|
+
static void setgroups_control(int action)
|
61
|
+
{
|
62
|
+
const char *file = _PATH_PROC_SETGROUPS;
|
63
|
+
const char *cmd;
|
64
|
+
int fd;
|
65
|
+
|
66
|
+
if (action < 0 || (size_t) action >= ARRAY_SIZE(setgroups_strings))
|
67
|
+
return;
|
68
|
+
cmd = setgroups_strings[action];
|
69
|
+
|
70
|
+
fd = open(file, O_WRONLY);
|
71
|
+
if (fd < 0) {
|
72
|
+
if (errno == ENOENT)
|
73
|
+
return;
|
74
|
+
err(EXIT_FAILURE, _("cannot open %s"), file);
|
75
|
+
}
|
76
|
+
|
77
|
+
if (write_all(fd, cmd, strlen(cmd)))
|
78
|
+
err(EXIT_FAILURE, _("write failed %s"), file);
|
79
|
+
close(fd);
|
80
|
+
}
|
81
|
+
|
82
|
+
static void map_id(const char *file, uint32_t from, uint32_t to)
|
83
|
+
{
|
84
|
+
char *buf;
|
85
|
+
int fd;
|
86
|
+
|
87
|
+
fd = open(file, O_WRONLY);
|
88
|
+
if (fd < 0)
|
89
|
+
err(EXIT_FAILURE, _("cannot open %s"), file);
|
90
|
+
|
91
|
+
xasprintf(&buf, "%u %u 1", from, to);
|
92
|
+
if (write_all(fd, buf, strlen(buf)))
|
93
|
+
err(EXIT_FAILURE, _("write failed %s"), file);
|
94
|
+
free(buf);
|
95
|
+
close(fd);
|
96
|
+
}
|
97
|
+
|
98
|
+
static void set_propagation(unsigned long flags)
|
99
|
+
{
|
100
|
+
if (flags == 0)
|
101
|
+
return;
|
102
|
+
|
103
|
+
if (mount("none", "/", NULL, flags, NULL) != 0)
|
104
|
+
err(EXIT_FAILURE, _("cannot change root filesystem propagation"));
|
105
|
+
}
|
106
|
+
|
107
|
+
static int set_ns_target(int type, const char *path)
|
108
|
+
{
|
109
|
+
struct namespace_file *ns;
|
110
|
+
|
111
|
+
for (ns = namespace_files; ns->name; ns++) {
|
112
|
+
if (ns->type != type)
|
113
|
+
continue;
|
114
|
+
ns->target = path;
|
115
|
+
npersists++;
|
116
|
+
return 0;
|
117
|
+
}
|
118
|
+
|
119
|
+
return -EINVAL;
|
120
|
+
}
|
121
|
+
|
122
|
+
static int bind_ns_files(pid_t pid)
|
123
|
+
{
|
124
|
+
struct namespace_file *ns;
|
125
|
+
char src[PATH_MAX];
|
126
|
+
|
127
|
+
for (ns = namespace_files; ns->name; ns++) {
|
128
|
+
if (!ns->target)
|
129
|
+
continue;
|
130
|
+
|
131
|
+
snprintf(src, sizeof(src), "/proc/%u/%s", (unsigned) pid, ns->name);
|
132
|
+
|
133
|
+
if (mount(src, ns->target, NULL, MS_BIND, NULL) != 0)
|
134
|
+
err(EXIT_FAILURE, _("mount %s on %s failed"), src, ns->target);
|
135
|
+
}
|
136
|
+
|
137
|
+
return 0;
|
138
|
+
}
|
139
|
+
|
140
|
+
static ino_t get_mnt_ino(pid_t pid)
|
141
|
+
{
|
142
|
+
struct stat st;
|
143
|
+
char path[PATH_MAX];
|
144
|
+
|
145
|
+
snprintf(path, sizeof(path), "/proc/%u/ns/mnt", (unsigned) pid);
|
146
|
+
|
147
|
+
if (stat(path, &st) != 0)
|
148
|
+
err(EXIT_FAILURE, _("stat of %s failed"), path);
|
149
|
+
return st.st_ino;
|
150
|
+
}
|
151
|
+
|
152
|
+
static void settime(time_t offset, clockid_t clk_id)
|
153
|
+
{
|
154
|
+
char buf[sizeof(stringify_value(ULONG_MAX)) * 3];
|
155
|
+
int fd, len;
|
156
|
+
|
157
|
+
len = snprintf(buf, sizeof(buf), "%d %ld 0", clk_id, offset);
|
158
|
+
|
159
|
+
fd = open("/proc/self/timens_offsets", O_WRONLY);
|
160
|
+
if (fd < 0)
|
161
|
+
err(EXIT_FAILURE, _("failed to open /proc/self/timens_offsets"));
|
162
|
+
|
163
|
+
if (write(fd, buf, len) != len)
|
164
|
+
err(EXIT_FAILURE, _("failed to write to /proc/self/timens_offsets"));
|
165
|
+
|
166
|
+
close(fd);
|
167
|
+
}
|
168
|
+
|
169
|
+
static void bind_ns_files_from_child(pid_t *child, int fds[2])
|
170
|
+
{
|
171
|
+
char ch;
|
172
|
+
pid_t ppid = getpid();
|
173
|
+
ino_t ino = get_mnt_ino(ppid);
|
174
|
+
|
175
|
+
if (pipe(fds) < 0)
|
176
|
+
err(EXIT_FAILURE, _("pipe failed"));
|
177
|
+
|
178
|
+
*child = fork();
|
179
|
+
|
180
|
+
switch (*child) {
|
181
|
+
case -1:
|
182
|
+
err(EXIT_FAILURE, _("fork failed"));
|
183
|
+
|
184
|
+
case 0: /* child */
|
185
|
+
close(fds[1]);
|
186
|
+
fds[1] = -1;
|
187
|
+
|
188
|
+
/* wait for parent */
|
189
|
+
if (read_all(fds[0], &ch, 1) != 1 && ch != PIPE_SYNC_BYTE)
|
190
|
+
err(EXIT_FAILURE, _("failed to read pipe"));
|
191
|
+
if (get_mnt_ino(ppid) == ino)
|
192
|
+
exit(EXIT_FAILURE);
|
193
|
+
bind_ns_files(ppid);
|
194
|
+
exit(EXIT_SUCCESS);
|
195
|
+
break;
|
196
|
+
|
197
|
+
default: /* parent */
|
198
|
+
close(fds[0]);
|
199
|
+
fds[0] = -1;
|
200
|
+
break;
|
201
|
+
}
|
202
|
+
}
|
203
|
+
|
204
|
+
int rb_unshare_internal(struct rb_unshare_args args)
|
205
|
+
{
|
206
|
+
int unshare_flags = 0;
|
207
|
+
|
208
|
+
// int kill_child_signo = 0; /* 0 means --kill-child was not used */
|
209
|
+
char *procmnt = NULL;
|
210
|
+
char *newroot = NULL;
|
211
|
+
char *newdir = NULL;
|
212
|
+
|
213
|
+
int fds[2];
|
214
|
+
int status;
|
215
|
+
|
216
|
+
int pid_bind = 0;
|
217
|
+
int pid = 0;
|
218
|
+
|
219
|
+
time_t monotonic = 0;
|
220
|
+
time_t boottime = 0;
|
221
|
+
|
222
|
+
uid_t real_euid = geteuid();
|
223
|
+
gid_t real_egid = getegid();
|
224
|
+
|
225
|
+
if (args.clone_newns) {
|
226
|
+
unshare_flags |= CLONE_NEWNS;
|
227
|
+
// if (optarg)
|
228
|
+
// set_ns_target(CLONE_NEWNS, optarg);
|
229
|
+
}
|
230
|
+
if (args.clone_newuts) {
|
231
|
+
unshare_flags |= CLONE_NEWUTS;
|
232
|
+
// if (optarg)
|
233
|
+
// set_ns_target(CLONE_NEWUTS, optarg);
|
234
|
+
}
|
235
|
+
if (args.clone_newipc) {
|
236
|
+
unshare_flags |= CLONE_NEWIPC;
|
237
|
+
// if (optarg)
|
238
|
+
// set_ns_target(CLONE_NEWIPC, optarg);
|
239
|
+
}
|
240
|
+
if (args.clone_newnet) {
|
241
|
+
unshare_flags |= CLONE_NEWNET;
|
242
|
+
// if (optarg)
|
243
|
+
// set_ns_target(CLONE_NEWNET, optarg);
|
244
|
+
}
|
245
|
+
if (args.clone_newpid) {
|
246
|
+
unshare_flags |= CLONE_NEWPID;
|
247
|
+
// if (optarg)
|
248
|
+
// set_ns_target(CLONE_NEWPID, optarg);
|
249
|
+
}
|
250
|
+
if (args.clone_newuser) {
|
251
|
+
unshare_flags |= CLONE_NEWUSER;
|
252
|
+
// if (optarg)
|
253
|
+
// set_ns_target(CLONE_NEWUSER, optarg);
|
254
|
+
}
|
255
|
+
if (args.clone_newcgroup) {
|
256
|
+
unshare_flags |= CLONE_NEWCGROUP;
|
257
|
+
// if (optarg)
|
258
|
+
// set_ns_target(CLONE_NEWCGROUP, optarg);
|
259
|
+
}
|
260
|
+
if (args.clone_newtime) {
|
261
|
+
unshare_flags |= CLONE_NEWTIME;
|
262
|
+
// if (optarg)
|
263
|
+
// set_ns_target(CLONE_NEWTIME, optarg);
|
264
|
+
}
|
265
|
+
if (args.mount_proc != Qundef) {
|
266
|
+
unshare_flags |= CLONE_NEWNS;
|
267
|
+
procmnt = StringValueCStr(args.mount_proc);
|
268
|
+
}
|
269
|
+
if (args.map_user != (uid_t) -1) {
|
270
|
+
unshare_flags |= CLONE_NEWUSER;
|
271
|
+
}
|
272
|
+
if (args.map_group != (gid_t) -1) {
|
273
|
+
unshare_flags |= CLONE_NEWUSER;
|
274
|
+
}
|
275
|
+
if (args.map_root_user) {
|
276
|
+
unshare_flags |= CLONE_NEWUSER;
|
277
|
+
args.map_user = 0;
|
278
|
+
args.map_group = 0;
|
279
|
+
}
|
280
|
+
if (args.map_current_user) {
|
281
|
+
unshare_flags |= CLONE_NEWUSER;
|
282
|
+
args.map_user = real_euid;
|
283
|
+
args.map_group = real_egid;
|
284
|
+
}
|
285
|
+
if (args.kill_child) {
|
286
|
+
args.fork = true;
|
287
|
+
// if (optarg) {
|
288
|
+
// if ((kill_child_signo = signame_to_signum(optarg)) < 0)
|
289
|
+
// errx(EXIT_FAILURE, _("unknown signal: %s"),
|
290
|
+
// optarg);
|
291
|
+
// } else {
|
292
|
+
// kill_child_signo = SIGKILL;
|
293
|
+
// }
|
294
|
+
}
|
295
|
+
if (args.keep_caps) {
|
296
|
+
cap_last_cap(); /* Force last cap to be cached before we fork. */
|
297
|
+
}
|
298
|
+
if (args.root != Qundef) {
|
299
|
+
newroot = StringValueCStr(args.root);
|
300
|
+
}
|
301
|
+
// case OPT_MONOTONIC:
|
302
|
+
// monotonic = strtoul_or_err(optarg, _("failed to parse monotonic offset"));
|
303
|
+
// force_monotonic = 1;
|
304
|
+
// break;
|
305
|
+
// case OPT_BOOTTIME:
|
306
|
+
// boottime = strtoul_or_err(optarg, _("failed to parse boottime offset"));
|
307
|
+
// force_boottime = 1;
|
308
|
+
// break;
|
309
|
+
|
310
|
+
if ((args.force_monotonic || args.force_boottime) && !(unshare_flags & CLONE_NEWTIME))
|
311
|
+
errx(EXIT_FAILURE, _("options --monotonic and --boottime require "
|
312
|
+
"unsharing of a time namespace (-t)"));
|
313
|
+
|
314
|
+
if (npersists && (unshare_flags & CLONE_NEWNS))
|
315
|
+
bind_ns_files_from_child(&pid_bind, fds);
|
316
|
+
|
317
|
+
if (-1 == unshare(unshare_flags))
|
318
|
+
err(EXIT_FAILURE, _("unshare failed"));
|
319
|
+
|
320
|
+
if (args.force_boottime)
|
321
|
+
settime(boottime, CLOCK_BOOTTIME);
|
322
|
+
|
323
|
+
if (args.force_monotonic)
|
324
|
+
settime(monotonic, CLOCK_MONOTONIC);
|
325
|
+
|
326
|
+
if (args.fork) {
|
327
|
+
/* force child forking before mountspace binding
|
328
|
+
* so pid_for_children is populated.
|
329
|
+
* Silence:
|
330
|
+
* warning: pthread_create failed for timer: Invalid argument, scheduling broken
|
331
|
+
* by setting $VERBOSE = nil.
|
332
|
+
* */
|
333
|
+
VALUE res = rb_eval_string("Process.fork");
|
334
|
+
pid = NIL_P(res) ? 0 : NUM2INT(res);
|
335
|
+
|
336
|
+
switch(pid) {
|
337
|
+
case -1:
|
338
|
+
err(EXIT_FAILURE, _("fork failed"));
|
339
|
+
case 0: /* child */
|
340
|
+
if (pid_bind && (unshare_flags & CLONE_NEWNS))
|
341
|
+
close(fds[1]);
|
342
|
+
break;
|
343
|
+
default: /* parent */
|
344
|
+
break;
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
if (npersists && (pid || !args.fork)) {
|
349
|
+
/* run in parent */
|
350
|
+
if (pid_bind && (unshare_flags & CLONE_NEWNS)) {
|
351
|
+
int rc;
|
352
|
+
char ch = PIPE_SYNC_BYTE;
|
353
|
+
|
354
|
+
/* signal child we are ready */
|
355
|
+
write_all(fds[1], &ch, 1);
|
356
|
+
close(fds[1]);
|
357
|
+
fds[1] = -1;
|
358
|
+
|
359
|
+
/* wait for bind_ns_files_from_child() */
|
360
|
+
do {
|
361
|
+
rc = NUM2INT(PIDT2NUM(rb_waitpid(pid_bind, &status, 0)));
|
362
|
+
if (rc < 0) {
|
363
|
+
if (errno == EINTR)
|
364
|
+
continue;
|
365
|
+
rb_sys_fail("rb_waitpid");
|
366
|
+
}
|
367
|
+
if (WIFEXITED(status) &&
|
368
|
+
WEXITSTATUS(status) != EXIT_SUCCESS) {
|
369
|
+
return NUM2PIDT(INT2NUM(pid));
|
370
|
+
}
|
371
|
+
} while (rc < 0);
|
372
|
+
} else {
|
373
|
+
/* simple way, just bind */
|
374
|
+
bind_ns_files(getpid());
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
if (pid) {
|
379
|
+
if (!args.wait) {
|
380
|
+
return NUM2PIDT(INT2NUM(pid));
|
381
|
+
}
|
382
|
+
|
383
|
+
if (NUM2INT(PIDT2NUM(rb_waitpid(pid, &status, 0))) == -1) {
|
384
|
+
rb_sys_fail("rb_waitpid");
|
385
|
+
}
|
386
|
+
|
387
|
+
if (WIFEXITED(status)) {
|
388
|
+
return NUM2PIDT(INT2NUM(pid));
|
389
|
+
}
|
390
|
+
|
391
|
+
if (WIFSIGNALED(status)) {
|
392
|
+
kill(getpid(), WTERMSIG(status));
|
393
|
+
}
|
394
|
+
|
395
|
+
err(EXIT_FAILURE, _("child exit failed"));
|
396
|
+
}
|
397
|
+
|
398
|
+
if (args.kill_child) {
|
399
|
+
// if (kill_child_signo != 0 && prctl(PR_SET_PDEATHSIG, kill_child_signo) < 0)
|
400
|
+
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
|
401
|
+
err(EXIT_FAILURE, "prctl failed");
|
402
|
+
}
|
403
|
+
|
404
|
+
if (args.map_user != (uid_t) -1)
|
405
|
+
map_id(_PATH_PROC_UIDMAP, args.map_user, real_euid);
|
406
|
+
|
407
|
+
/* Since Linux 3.19 unprivileged writing of /proc/self/gid_map
|
408
|
+
* has been disabled unless /proc/self/setgroups is written
|
409
|
+
* first to permanently disable the ability to call setgroups
|
410
|
+
* in that user namespace. */
|
411
|
+
if (args.map_group != (gid_t) -1) {
|
412
|
+
if (args.set_groups == SETGROUPS_ALLOW)
|
413
|
+
errx(EXIT_FAILURE, _("options --setgroups=allow and "
|
414
|
+
"--map-group are mutually exclusive"));
|
415
|
+
setgroups_control(SETGROUPS_DENY);
|
416
|
+
map_id(_PATH_PROC_GIDMAP, args.map_group, real_egid);
|
417
|
+
}
|
418
|
+
|
419
|
+
if (args.set_groups != SETGROUPS_NONE)
|
420
|
+
setgroups_control(args.set_groups);
|
421
|
+
|
422
|
+
if ((unshare_flags & CLONE_NEWNS) && args.propagation)
|
423
|
+
set_propagation(args.propagation);
|
424
|
+
|
425
|
+
if (newroot) {
|
426
|
+
if (chroot(newroot) != 0)
|
427
|
+
err(EXIT_FAILURE,
|
428
|
+
_("cannot change root directory to '%s'"), newroot);
|
429
|
+
newdir = newdir ?: "/";
|
430
|
+
}
|
431
|
+
if (newdir && chdir(newdir))
|
432
|
+
err(EXIT_FAILURE, _("cannot chdir to '%s'"), newdir);
|
433
|
+
|
434
|
+
if (procmnt) {
|
435
|
+
/* When not changing root and using the default propagation flags
|
436
|
+
then the recursive propagation change of root will
|
437
|
+
automatically change that of an existing proc mount. */
|
438
|
+
if (!newroot && args.propagation != (MS_PRIVATE|MS_REC)) {
|
439
|
+
int rc = mount("none", procmnt, NULL, MS_PRIVATE|MS_REC, NULL);
|
440
|
+
|
441
|
+
/* Custom procmnt means that proc is very likely not mounted, causing EINVAL.
|
442
|
+
Ignoring the error in this specific instance is considered safe. */
|
443
|
+
if(rc != 0 && errno != EINVAL)
|
444
|
+
err(EXIT_FAILURE, _("cannot change %s filesystem propagation"), procmnt);
|
445
|
+
}
|
446
|
+
|
447
|
+
if (mount("proc", procmnt, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) != 0)
|
448
|
+
err(EXIT_FAILURE, _("mount %s failed"), procmnt);
|
449
|
+
}
|
450
|
+
|
451
|
+
if (args.set_gid) {
|
452
|
+
if (setgroups(0, NULL) != 0) /* drop supplementary groups */
|
453
|
+
err(EXIT_FAILURE, _("setgroups failed"));
|
454
|
+
if (setgid(args.set_gid) < 0) /* change GID */
|
455
|
+
err(EXIT_FAILURE, _("setgid failed"));
|
456
|
+
}
|
457
|
+
if (args.set_uid && setuid(args.set_uid) < 0) /* change UID */
|
458
|
+
err(EXIT_FAILURE, _("setuid failed"));
|
459
|
+
|
460
|
+
/* We use capabilities system calls to propagate the permitted
|
461
|
+
* capabilities into the ambient set because we have already
|
462
|
+
* forked so are in async-signal-safe context. */
|
463
|
+
if (args.keep_caps && (unshare_flags & CLONE_NEWUSER)) {
|
464
|
+
struct __user_cap_header_struct header = {
|
465
|
+
.version = _LINUX_CAPABILITY_VERSION_3,
|
466
|
+
.pid = 0,
|
467
|
+
};
|
468
|
+
|
469
|
+
struct __user_cap_data_struct payload[_LINUX_CAPABILITY_U32S_3] = {{ 0 }};
|
470
|
+
uint64_t effective, cap;
|
471
|
+
|
472
|
+
if (capget(&header, payload) < 0)
|
473
|
+
err(EXIT_FAILURE, _("capget failed"));
|
474
|
+
|
475
|
+
/* In order the make capabilities ambient, we first need to ensure
|
476
|
+
* that they are all inheritable. */
|
477
|
+
payload[0].inheritable = payload[0].permitted;
|
478
|
+
payload[1].inheritable = payload[1].permitted;
|
479
|
+
|
480
|
+
if (capset(&header, payload) < 0)
|
481
|
+
err(EXIT_FAILURE, _("capset failed"));
|
482
|
+
|
483
|
+
effective = ((uint64_t)payload[1].effective << 32) | (uint64_t)payload[0].effective;
|
484
|
+
|
485
|
+
for (cap = 0; cap < (sizeof(effective) * 8); cap++) {
|
486
|
+
/* This is the same check as cap_valid(), but using
|
487
|
+
* the runtime value for the last valid cap. */
|
488
|
+
if (cap > (uint64_t) cap_last_cap())
|
489
|
+
continue;
|
490
|
+
|
491
|
+
if ((effective & (1 << cap))
|
492
|
+
&& prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0)
|
493
|
+
err(EXIT_FAILURE, _("prctl(PR_CAP_AMBIENT) failed"));
|
494
|
+
}
|
495
|
+
}
|
496
|
+
|
497
|
+
return NUM2PIDT(INT2NUM(pid));
|
498
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#ifndef UNSHARE_H
|
2
|
+
#define UNSHARE_H 1
|
3
|
+
|
4
|
+
#include <stdbool.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
|
7
|
+
#include "include/c.h"
|
8
|
+
|
9
|
+
#undef _
|
10
|
+
# define _(Text) (Text)
|
11
|
+
|
12
|
+
struct rb_unshare_args {
|
13
|
+
bool clone_newuser;
|
14
|
+
bool clone_newcgroup;
|
15
|
+
bool clone_newipc;
|
16
|
+
bool clone_newuts;
|
17
|
+
bool clone_newnet;
|
18
|
+
bool clone_newpid;
|
19
|
+
bool clone_newns;
|
20
|
+
bool clone_newtime;
|
21
|
+
|
22
|
+
bool fork;
|
23
|
+
bool wait;
|
24
|
+
|
25
|
+
// default: /proc
|
26
|
+
VALUE mount_proc;
|
27
|
+
VALUE root;
|
28
|
+
|
29
|
+
VALUE new_dir;
|
30
|
+
bool map_root_user;
|
31
|
+
bool map_current_user;
|
32
|
+
uid_t map_user;
|
33
|
+
gid_t map_group;
|
34
|
+
bool keep_caps;
|
35
|
+
uid_t set_uid;
|
36
|
+
gid_t set_gid;
|
37
|
+
int set_groups;
|
38
|
+
unsigned long propagation;
|
39
|
+
bool force_boottime;
|
40
|
+
bool force_monotonic;
|
41
|
+
bool kill_child;
|
42
|
+
};
|
43
|
+
|
44
|
+
int rb_unshare_internal(struct rb_unshare_args args);
|
45
|
+
|
46
|
+
/* synchronize parent and child by pipe */
|
47
|
+
#define PIPE_SYNC_BYTE 0x06
|
48
|
+
|
49
|
+
/* 'private' is kernel default */
|
50
|
+
#define UNSHARE_PROPAGATION_DEFAULT (MS_REC | MS_PRIVATE)
|
51
|
+
|
52
|
+
enum {
|
53
|
+
SETGROUPS_NONE = -1,
|
54
|
+
SETGROUPS_DENY = 0,
|
55
|
+
SETGROUPS_ALLOW = 1,
|
56
|
+
};
|
57
|
+
|
58
|
+
static const char *setgroups_strings[] =
|
59
|
+
{
|
60
|
+
[SETGROUPS_DENY] = "deny",
|
61
|
+
[SETGROUPS_ALLOW] = "allow"
|
62
|
+
};
|
63
|
+
|
64
|
+
#ifndef XALLOC_EXIT_CODE
|
65
|
+
# define XALLOC_EXIT_CODE EXIT_FAILURE
|
66
|
+
#endif
|
67
|
+
|
68
|
+
static inline
|
69
|
+
__attribute__((__format__(printf, 2, 3)))
|
70
|
+
int xasprintf(char **strp, const char *fmt, ...) {
|
71
|
+
int ret;
|
72
|
+
va_list args;
|
73
|
+
|
74
|
+
va_start(args, fmt);
|
75
|
+
ret = vasprintf(&(*strp), fmt, args);
|
76
|
+
va_end(args);
|
77
|
+
if (ret < 0)
|
78
|
+
err(XALLOC_EXIT_CODE, "cannot allocate string");
|
79
|
+
return ret;
|
80
|
+
}
|
81
|
+
|
82
|
+
#endif
|
data/lib/runshare.rb
ADDED
data/runshare.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "runshare/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "runshare"
|
8
|
+
spec.version = RUnshare::VERSION
|
9
|
+
spec.authors = ["Ivan Prisyazhnyy"]
|
10
|
+
spec.email = ["john.koepi@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "This tool allows to unshare Linux namespaces."
|
13
|
+
spec.description = File.read("README.md")
|
14
|
+
spec.homepage = "https://github.com/sitano/runshare"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata["allowed_push_host"] = "Set to 'http://mygemserver.com'"
|
21
|
+
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/sitano/runshare"
|
24
|
+
# spec.metadata["changelog_uri"] = "Put your gem's CHANGELOG.md URL here."
|
25
|
+
else
|
26
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
"public gem pushes."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
spec.extensions = ["ext/runshare/extconf.rb"]
|
39
|
+
|
40
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
41
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
42
|
+
spec.add_development_dependency "rake-compiler"
|
43
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
44
|
+
end
|