GNU bug report logs - #34859
[PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.

Previous Next

Package: guix-patches;

Reported by: Ludovic Courtès <ludo <at> gnu.org>

Date: Thu, 14 Mar 2019 16:11:02 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 34859 in the body.
You can then email your comments to 34859 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#34859; Package guix-patches. (Thu, 14 Mar 2019 16:11:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ludovic Courtès <ludo <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Thu, 14 Mar 2019 16:11:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: Ludovic Courtès <ludovic.courtes <at> inria.fr>
Subject: [PATCH] pack: "-RR" produces PRoot-enabled relocatable binaries.
Date: Thu, 14 Mar 2019 17:10:26 +0100
From: Ludovic Courtès <ludovic.courtes <at> 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 <at> gnu.org>
+   Copyright (C) 2018, 2019 Ludovic Courtès <ludo <at> gnu.org>
 
    This file is part of GNU Guix.
 
@@ -212,6 +212,46 @@ disallow_setgroups (pid_t pid)
 }
 
 
+#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
+
+
 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 <at> gnu.org>
+# Copyright © 2018, 2019 Ludovic Courtès <ludo <at> 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





Information forwarded to guix-patches <at> gnu.org:
bug#34859; Package guix-patches. (Fri, 15 Mar 2019 13:43:02 GMT) Full text and rfc822 format available.

Message #8 received at 34859 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludovic.courtes <at> inria.fr>
To: 34859 <at> debbugs.gnu.org
Subject: Re: [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled
 relocatable binaries.
Date: Fri, 15 Mar 2019 14:41:48 +0100
Hi there!

Ludovic Courtès <ludo <at> 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




Information forwarded to guix-patches <at> gnu.org:
bug#34859; Package guix-patches. (Fri, 15 Mar 2019 14:26:01 GMT) Full text and rfc822 format available.

Message #11 received at 34859 <at> debbugs.gnu.org (full text, mbox):

From: Julien Lepiller <julien <at> lepiller.eu>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: 34859 <at> debbugs.gnu.org
Subject: Re: [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled
 relocatable binaries.
Date: Fri, 15 Mar 2019 15:24:54 +0100
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 <at> 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.





Information forwarded to guix-patches <at> gnu.org:
bug#34859; Package guix-patches. (Fri, 15 Mar 2019 14:45:01 GMT) Full text and rfc822 format available.

Message #14 received at 34859 <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludovic.courtes <at> inria.fr>
To: Julien Lepiller <julien <at> lepiller.eu>
Cc: 34859 <at> debbugs.gnu.org
Subject: Re: [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled
 relocatable binaries.
Date: Fri, 15 Mar 2019 15:44:43 +0100
Hello!

Julien Lepiller <julien <at> 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’.




Information forwarded to guix-patches <at> gnu.org:
bug#34859; Package guix-patches. (Fri, 15 Mar 2019 16:06:02 GMT) Full text and rfc822 format available.

Message #17 received at 34859 <at> debbugs.gnu.org (full text, mbox):

From: Ricardo Wurmus <rekado <at> elephly.net>
To: Ludovic Courtès <ludo <at> gnu.org>
Cc: Ludovic Courtès <ludovic.courtes <at> inria.fr>,
 34859 <at> debbugs.gnu.org
Subject: Re: [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled
 relocatable binaries.
Date: Fri, 15 Mar 2019 17:04:51 +0100
Ludovic Courtès <ludo <at> 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





Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Fri, 15 Mar 2019 22:36:01 GMT) Full text and rfc822 format available.

Notification sent to Ludovic Courtès <ludo <at> gnu.org>:
bug acknowledged by developer. (Fri, 15 Mar 2019 22:36:02 GMT) Full text and rfc822 format available.

Message #22 received at 34859-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: Ricardo Wurmus <rekado <at> elephly.net>
Cc: 34859-done <at> debbugs.gnu.org
Subject: Re: [bug#34859] [PATCH] pack: "-RR" produces PRoot-enabled
 relocatable binaries.
Date: Fri, 15 Mar 2019 23:34:53 +0100
Ricardo Wurmus <rekado <at> elephly.net> skribis:

> Ludovic Courtès <ludo <at> 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’.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sat, 13 Apr 2019 11:24:05 GMT) Full text and rfc822 format available.

This bug report was last modified 5 years and 8 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.