unofficial mirror of guix-patches@gnu.org 
 help / color / mirror / code / Atom feed
* [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
@ 2019-03-14 16:10 Ludovic Courtès
  2019-03-15 13:41 ` Ludovic Courtès
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Ludovic Courtès @ 2019-03-14 16:10 UTC (permalink / raw)
  To: 34859; +Cc: Ludovic Courtès

From: Ludovic Courtès <ludovic.courtes@inria.fr>

* gnu/packages/aux-files/run-in-namespace.c (exec_with_proot): New
function.
(main): When 'clone' fails, call 'rm_rf'.
[PROOT_PROGRAM]: When 'clone' fails, call 'exec_with_proot'.
* guix/scripts/pack.scm (wrapped-package): Add #:proot?.
[proot]: New procedure.
[build]: Compile with -DPROOT_PROGRAM when PROOT? is true.
* guix/scripts/pack.scm (%options): Set the 'relocatable?' value to
'proot when "-R" is passed several times.
(guix-pack): Pass #:proot? to 'wrapped-package'.
* tests/guix-pack-relocatable.sh: Use "-RR" on Intel systems that lack
user namespace support.
* doc/guix.texi (Invoking guix pack): Document -RR.
---
 doc/guix.texi                             | 39 ++++++++++++++-----
 gnu/packages/aux-files/run-in-namespace.c | 47 ++++++++++++++++++++++-
 guix/scripts/pack.scm                     | 33 +++++++++++++---
 tests/guix-pack-relocatable.sh            | 21 +++++++---
 4 files changed, 119 insertions(+), 21 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 043aad1b65..3a6a35b9c6 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -4760,14 +4760,24 @@ symlinks, as well as empty mount points for virtual file systems like
 procfs.
 @end table
 
+@cindex relocatable binaries
 @item --relocatable
 @itemx -R
 Produce @dfn{relocatable binaries}---i.e., binaries that can be placed
-anywhere in the file system hierarchy and run from there.  For example,
-if you create a pack containing Bash with:
+anywhere in the file system hierarchy and run from there.
+
+When this option is passed once, the resulting binaries require support for
+@dfn{user namespaces} in the kernel Linux; when passed
+@emph{twice}@footnote{Here's a trick to memorize it: @code{-RR}, which adds
+PRoot support, can be thought of as the abbreviation of ``Really
+Relocatable''.  Neat, isn't it?}, relocatable binaries fall to back to PRoot
+if user namespaces are unavailable, and essentially work anywhere---see below
+for the implications.
+
+For example, if you create a pack containing Bash with:
 
 @example
-guix pack -R -S /mybin=bin bash
+guix pack -RR -S /mybin=bin bash
 @end example
 
 @noindent
@@ -4786,12 +4796,23 @@ In that shell, if you type @code{ls /gnu/store}, you'll notice that
 altogether!  That is probably the simplest way to deploy Guix-built
 software on a non-Guix machine.
 
-There's a gotcha though: this technique relies on the @dfn{user
-namespace} feature of the kernel Linux, which allows unprivileged users
-to mount or change root.  Old versions of Linux did not support it, and
-some GNU/Linux distributions turn it off; on these systems, programs
-from the pack @emph{will fail to run}, unless they are unpacked in the
-root file system.
+@quotation Note
+By default, relocatable binaries rely on the @dfn{user namespace} feature of
+the kernel Linux, which allows unprivileged users to mount or change root.
+Old versions of Linux did not support it, and some GNU/Linux distributions
+turn it off.
+
+To produce relocatable binaries that work even in the absence of user
+namespaces, pass @option{--relocatable} or @option{-R} @emph{twice}.  In that
+case, binaries will try user namespace support and fall back to PRoot if user
+namespaces are not supported.
+
+The @uref{https://proot-me.github.io/, PRoot} program provides the necessary
+support for file system virtualization.  It achieves that by using the
+@code{ptrace} system call on the running program.  This approach has the
+advantage to work without requiring special kernel support, but it incurs
+run-time overhead every time a system call is made.
+@end quotation
 
 @item --expression=@var{expr}
 @itemx -e @var{expr}
diff --git a/gnu/packages/aux-files/run-in-namespace.c b/gnu/packages/aux-files/run-in-namespace.c
index f0cff88552..551f4db88a 100644
--- a/gnu/packages/aux-files/run-in-namespace.c
+++ b/gnu/packages/aux-files/run-in-namespace.c
@@ -1,5 +1,5 @@
 /* GNU Guix --- Functional package management for GNU
-   Copyright (C) 2018 Ludovic Courtès <ludo@gnu.org>
+   Copyright (C) 2018, 2019 Ludovic Courtès <ludo@gnu.org>
 
    This file is part of GNU Guix.
 
@@ -212,6 +212,46 @@ disallow_setgroups (pid_t pid)
 }
 
 \f
+#ifdef PROOT_PROGRAM
+
+/* Execute the wrapped program with PRoot, passing it ARGC and ARGV, and
+   "bind-mounting" STORE in the right place.  */
+static void
+exec_with_proot (const char *store, int argc, char *argv[])
+{
+  int proot_specific_argc = 4;
+  int proot_argc = argc + proot_specific_argc;
+  char *proot_argv[proot_argc], *proot;
+  char bind_spec[strlen (store) + 1 + sizeof "@STORE_DIRECTORY@"];
+
+  strcpy (bind_spec, store);
+  strcat (bind_spec, ":");
+  strcat (bind_spec, "@STORE_DIRECTORY@");
+
+  proot = concat (store, PROOT_PROGRAM);
+
+  proot_argv[0] = proot;
+  proot_argv[1] = "-b";
+  proot_argv[2] = bind_spec;
+  proot_argv[3] = "@WRAPPED_PROGRAM@";
+
+  for (int i = 0; i < argc; i++)
+    proot_argv[i + proot_specific_argc] = argv[i + 1];
+
+  proot_argv[proot_argc] = NULL;
+
+  /* Seccomp support seems to invariably lead to segfaults; disable it by
+     default.  */
+  setenv ("PROOT_NO_SECCOMP", "1", 0);
+
+  int err = execv (proot, proot_argv);
+  if (err < 0)
+    assert_perror (errno);
+}
+
+#endif
+
+\f
 int
 main (int argc, char *argv[])
 {
@@ -274,6 +314,10 @@ main (int argc, char *argv[])
 	  break;
 
 	case -1:
+	  rm_rf (new_root);
+#ifdef PROOT_PROGRAM
+	  exec_with_proot (store, argc, argv);
+#else
 	  fprintf (stderr, "%s: error: 'clone' failed: %m\n", argv[0]);
 	  fprintf (stderr, "\
 This may be because \"user namespaces\" are not supported on this system.\n\
@@ -281,6 +325,7 @@ Consequently, we cannot run '@WRAPPED_PROGRAM@',\n\
 unless you move it to the '@STORE_DIRECTORY@' directory.\n\
 \n\
 Please refer to the 'guix pack' documentation for more information.\n");
+#endif
 	  return EXIT_FAILURE;
 
 	default:
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index e2ecddfbfc..bfb8b85356 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -517,10 +517,14 @@ please email '~a'~%")
 ;;;
 
 (define* (wrapped-package package
-                          #:optional (compiler (c-compiler)))
+                          #:optional (compiler (c-compiler))
+                          #:key proot?)
   (define runner
     (local-file (search-auxiliary-file "run-in-namespace.c")))
 
+  (define (proot)
+    (specification->package "proot-static"))
+
   (define build
     (with-imported-modules (source-module-closure
                             '((guix build utils)
@@ -550,10 +554,19 @@ please email '~a'~%")
               (("@STORE_DIRECTORY@") (%store-directory)))
 
             (let* ((base   (strip-store-prefix program))
-                   (result (string-append #$output "/" base)))
+                   (result (string-append #$output "/" base))
+                   (proot  #$(and proot?
+                                  #~(string-drop
+                                     #$(file-append (proot) "/bin/proot")
+                                     (+ (string-length (%store-directory))
+                                        1)))))
               (mkdir-p (dirname result))
-              (invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
-                      "run.c" "-o" result)
+              (apply invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
+                     "run.c" "-o" result
+                     (if proot
+                         (list (string-append "-DPROOT_PROGRAM=\""
+                                              proot "\""))
+                         '()))
               (delete-file "run.c")))
 
           (setvbuf (current-output-port) 'line)
@@ -646,7 +659,12 @@ please email '~a'~%")
                    (exit 0)))
          (option '(#\R "relocatable") #f #f
                  (lambda (opt name arg result)
-                   (alist-cons 'relocatable? #t result)))
+                   (match (assq-ref result 'relocatable?)
+                     (#f
+                      (alist-cons 'relocatable? #t result))
+                     (_
+                      (alist-cons 'relocatable? 'proot
+                                  (alist-delete 'relocatable? result))))))
          (option '(#\e "expression") #t #f
                  (lambda (opt name arg result)
                    (alist-cons 'expression arg result)))
@@ -821,11 +839,14 @@ Create a bundle of PACKAGE.\n"))
                                           #:graft? (assoc-ref opts 'graft?))))
           (let* ((dry-run?    (assoc-ref opts 'dry-run?))
                  (relocatable? (assoc-ref opts 'relocatable?))
+                 (proot?      (eq? relocatable? 'proot))
                  (manifest    (let ((manifest (manifest-from-args store opts)))
                                 ;; Note: We cannot honor '--bootstrap' here because
                                 ;; 'glibc-bootstrap' lacks 'libc.a'.
                                 (if relocatable?
-                                    (map-manifest-entries wrapped-package manifest)
+                                    (map-manifest-entries
+                                     (cut wrapped-package <> #:proot? proot?)
+                                     manifest)
                                     manifest)))
                  (pack-format (assoc-ref opts 'format))
                  (name        (string-append (symbol->string pack-format)
diff --git a/tests/guix-pack-relocatable.sh b/tests/guix-pack-relocatable.sh
index 554416627b..38dcf1e485 100644
--- a/tests/guix-pack-relocatable.sh
+++ b/tests/guix-pack-relocatable.sh
@@ -1,5 +1,5 @@
 # GNU Guix --- Functional package management for GNU
-# Copyright © 2018 Ludovic Courtès <ludo@gnu.org>
+# Copyright © 2018, 2019 Ludovic Courtès <ludo@gnu.org>
 #
 # This file is part of GNU Guix.
 #
@@ -41,17 +41,28 @@ STORE_PARENT="`dirname $NIX_STORE_DIR`"
 export STORE_PARENT
 if test "$STORE_PARENT" = "/"; then exit 77; fi
 
-# This test requires user namespaces and associated command-line tools.
-if ! unshare -mrf sh -c 'mount -t tmpfs none "$STORE_PARENT"'
+if unshare -mrf sh -c 'mount -t tmpfs none "$STORE_PARENT"'
 then
-    exit 77
+    # Test the wrapper that relies on user namespaces.
+    relocatable_option="-R"
+else
+    case "`uname -m`" in
+	x86_64|i?86)
+	    # Test the wrapper that falls back to PRoot.
+	    relocatable_option="-RR";;
+	*)
+	    # XXX: Our 'proot' package currently fails tests on non-Intel
+	    # architectures, so skip this by default.
+	    exit 77;;
+    esac
 fi
 
 test_directory="`mktemp -d`"
 export test_directory
 trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
 
-tarball="`guix pack -R -S /Bin=bin sed`"
+export relocatable_option
+tarball="`guix pack $relocatable_option -S /Bin=bin sed`"
 (cd "$test_directory"; tar xvf "$tarball")
 
 # Run that relocatable 'sed' in a user namespace where we "erase" the store by
-- 
2.21.0

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
  2019-03-14 16:10 [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries Ludovic Courtès
@ 2019-03-15 13:41 ` Ludovic Courtès
  2019-03-15 14:24 ` Julien Lepiller
  2019-03-15 16:04 ` Ricardo Wurmus
  2 siblings, 0 replies; 6+ messages in thread
From: Ludovic Courtès @ 2019-03-15 13:41 UTC (permalink / raw)
  To: 34859

Hi there!

Ludovic Courtès <ludo@gnu.org> skribis:

>  @item --relocatable
>  @itemx -R
>  Produce @dfn{relocatable binaries}---i.e., binaries that can be placed
> -anywhere in the file system hierarchy and run from there.  For example,
> -if you create a pack containing Bash with:
> +anywhere in the file system hierarchy and run from there.
> +
> +When this option is passed once, the resulting binaries require support for
> +@dfn{user namespaces} in the kernel Linux; when passed
> +@emph{twice}@footnote{Here's a trick to memorize it: @code{-RR}, which adds
> +PRoot support, can be thought of as the abbreviation of ``Really
> +Relocatable''.  Neat, isn't it?}, relocatable binaries fall to back to PRoot
> +if user namespaces are unavailable, and essentially work anywhere---see below
> +for the implications.

For the record, we had discussed this idea a while back¹, and I was
recently reminded of it when looking at udocker².

Udocker has a third method to achieve file system virtualization, which
is to use Debian’s Fakechroot³.  Fakechroot is an LD_PRELOAD-based
thing, so it’s more lightweight than PRoot but also more fragile.  I
don’t think it’d be interesting for us to support that method in
addition to user namespaces and PRoot.

Thoughts?

Ludo’.

¹ https://lists.gnu.org/archive/html/guix-devel/2018-04/msg00252.html
² https://github.com/indigo-dc/udocker/
³ https://github.com/dex4er/fakechroot/wiki

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
  2019-03-14 16:10 [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries Ludovic Courtès
  2019-03-15 13:41 ` Ludovic Courtès
@ 2019-03-15 14:24 ` Julien Lepiller
  2019-03-15 14:44   ` Ludovic Courtès
  2019-03-15 16:04 ` Ricardo Wurmus
  2 siblings, 1 reply; 6+ messages in thread
From: Julien Lepiller @ 2019-03-15 14:24 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 34859

How does it work? do you look for a proot on the system where the pack
is unpacked, or is it included in the pack? If so, how does it work,
since I guess it can't be wrapped?

One small issue in the manual:

Le 2019-03-14 17:10, Ludovic Courtès a écrit :
> From: Ludovic Courtès <ludovic.courtes@inria.fr>
> 
> [...]
> 
> +@emph{twice}@footnote{Here's a trick to memorize it: @code{-RR}, which 
> adds
> +PRoot support, can be thought of as the abbreviation of ``Really
> +Relocatable''.  Neat, isn't it?}, relocatable binaries fall to back to 
> PRoot
                                                                ^ this 
here
> +if user namespaces are unavailable, and essentially work 
> anywhere---see below
> +for the implications.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
  2019-03-15 14:24 ` Julien Lepiller
@ 2019-03-15 14:44   ` Ludovic Courtès
  0 siblings, 0 replies; 6+ messages in thread
From: Ludovic Courtès @ 2019-03-15 14:44 UTC (permalink / raw)
  To: Julien Lepiller; +Cc: 34859

Hello!

Julien Lepiller <julien@lepiller.eu> skribis:

> How does it work? do you look for a proot on the system where the pack
> is unpacked, or is it included in the pack?

The pack includes ‘proot-static’, which takes approximately 1 MiB.  The
‘run-in-namespace.c’ wrapper determines its own location via
/proc/self/exe; from there it determines the location of the unpacked
store, and then determines the location of the statically-linked ‘proot’
program.

So it basically automates the PRoot trick described at
<https://guix-hpc.bordeaux.inria.fr/blog/2017/10/using-guix-without-being-root/>.

Ludo’.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
  2019-03-14 16:10 [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries Ludovic Courtès
  2019-03-15 13:41 ` Ludovic Courtès
  2019-03-15 14:24 ` Julien Lepiller
@ 2019-03-15 16:04 ` Ricardo Wurmus
  2019-03-15 22:34   ` bug#34859: " Ludovic Courtès
  2 siblings, 1 reply; 6+ messages in thread
From: Ricardo Wurmus @ 2019-03-15 16:04 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: Ludovic Courtès, 34859


Ludovic Courtès <ludo@gnu.org> writes:

> * gnu/packages/aux-files/run-in-namespace.c (exec_with_proot): New
> function.
> (main): When 'clone' fails, call 'rm_rf'.
> [PROOT_PROGRAM]: When 'clone' fails, call 'exec_with_proot'.
> * guix/scripts/pack.scm (wrapped-package): Add #:proot?.
> [proot]: New procedure.
> [build]: Compile with -DPROOT_PROGRAM when PROOT? is true.
> * guix/scripts/pack.scm (%options): Set the 'relocatable?' value to
> 'proot when "-R" is passed several times.
> (guix-pack): Pass #:proot? to 'wrapped-package'.
> * tests/guix-pack-relocatable.sh: Use "-RR" on Intel systems that lack
> user namespace support.
> * doc/guix.texi (Invoking guix pack): Document -RR.

This is great!

So, the only downside to using “-RR” is that it’s 1MB heavier than “-R”
due to the included proot-static?  Neat!

--
Ricardo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* bug#34859: [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
  2019-03-15 16:04 ` Ricardo Wurmus
@ 2019-03-15 22:34   ` Ludovic Courtès
  0 siblings, 0 replies; 6+ messages in thread
From: Ludovic Courtès @ 2019-03-15 22:34 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: 34859-done

Ricardo Wurmus <rekado@elephly.net> skribis:

> Ludovic Courtès <ludo@gnu.org> writes:
>
>> * gnu/packages/aux-files/run-in-namespace.c (exec_with_proot): New
>> function.
>> (main): When 'clone' fails, call 'rm_rf'.
>> [PROOT_PROGRAM]: When 'clone' fails, call 'exec_with_proot'.
>> * guix/scripts/pack.scm (wrapped-package): Add #:proot?.
>> [proot]: New procedure.
>> [build]: Compile with -DPROOT_PROGRAM when PROOT? is true.
>> * guix/scripts/pack.scm (%options): Set the 'relocatable?' value to
>> 'proot when "-R" is passed several times.
>> (guix-pack): Pass #:proot? to 'wrapped-package'.
>> * tests/guix-pack-relocatable.sh: Use "-RR" on Intel systems that lack
>> user namespace support.
>> * doc/guix.texi (Invoking guix pack): Document -RR.
>
> This is great!
>
> So, the only downside to using “-RR” is that it’s 1MB heavier than “-R”
> due to the included proot-static?  

Yes!  But note that our ‘proot-static’ package currently fails to build
on ARM.

> Neat!

Pushed as 99aec37a78e7be6a591d0e5b7439896d669a75d1, thanks!

Ludo’.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2019-03-15 22:48 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-03-14 16:10 [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries Ludovic Courtès
2019-03-15 13:41 ` Ludovic Courtès
2019-03-15 14:24 ` Julien Lepiller
2019-03-15 14:44   ` Ludovic Courtès
2019-03-15 16:04 ` Ricardo Wurmus
2019-03-15 22:34   ` bug#34859: " Ludovic Courtès

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).