Subject: bug#42173: [PATCH 2/2] services: nix: Fix sandbox.

From: Oleg Pykhalov <[email-masked]>
Date: Wed, 22 Jul 2020 09:59:39 +0300

* gnu/tests/package-management.scm: New file.
* gnu/local.mk: Add this.
* gnu/services/nix.scm (<nix-configuration>): New record.
(nix-activation): Generate Nix config file.
(nix-service-type): Add default value.
(nix-shepherd-service): Allow provide Nix package.
* doc/guix.texi (Miscellaneous Services)[Nix service]<nix-configuration>:
Document record.
---
 doc/guix.texi                    |  21 +++++
 gnu/local.mk                     |   1 +
 gnu/services/nix.scm             |  90 +++++++++++++--------
 gnu/tests/package-management.scm | 131 +++++++++++++++++++++++++++++++
 4 files changed, 211 insertions(+), 32 deletions(-)
 create mode 100644 gnu/tests/package-management.scm

diff --git a/doc/guix.texi b/doc/guix.texi
index 26ef937604..5639a360be 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -27597,6 +27597,27 @@ $ source /run/current-system/profile/etc/profile.d/nix.sh
 
 @end defvr
 
+@deftp {Data Type} nix-configuration
+This data type represents the configuration of the Nix daemon.
+
+@table @asis
+@item @code{nix} (default: @code{nix})
+The Nix package to use.
+
+@item @code{sandbox} (default: @code{#t})
+Specifies whether builds are sandboxed by default.
+
+@item @code{build-sandbox-paths} (default: @code{'()})
+This is a list of strings or objects appended to the
+@code{build-sandbox-paths} field of the configuration file.
+
+@item @code{extra-config} (default: @code{'()})
+This is a list of strings or objects appended to the configuration file.
+It is used to pass extra text to be added verbatim to the configuration
+file.
+@end table
+@end deftp
+
 @node Setuid Programs
 @section Setuid Programs
 
diff --git a/gnu/local.mk b/gnu/local.mk
index 0eac01d72d..2c19562171 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -674,6 +674,7 @@ GNU_SYSTEM_MODULES =				\
   %D%/tests/mail.scm				\
   %D%/tests/messaging.scm			\
   %D%/tests/networking.scm			\
+  %D%/tests/package-management.scm		\
   %D%/tests/reconfigure.scm			\
   %D%/tests/rsync.scm				\
   %D%/tests/security-token.scm			\
diff --git a/gnu/services/nix.scm b/gnu/services/nix.scm
index 3c0065207d..04e7726e4d 100644
--- a/gnu/services/nix.scm
+++ b/gnu/services/nix.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2019 Oleg Pykhalov <[email-masked]>
+;;; Copyright © 2019, 2020 Oleg Pykhalov <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -31,7 +31,9 @@
   #:use-module (guix store)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
+  #:use-module (ice-9 match)
   #:use-module (ice-9 format)
+  #:use-module (guix modules)
   #:export (nix-service-type))
 
 ;;; Commentary:
@@ -40,10 +42,17 @@
 ;;;
 ;;; Code:
 
-
-;;;
-;;; Accounts
-;;;
+(define-record-type* <nix-configuration>
+  nix-configuration make-nix-configuration
+  nix-configuration?
+  (package             nix-configuration-package ;package
+                       (default nix))
+  (sandbox             nix-configuration-sandbox ;boolean
+                       (default #t))
+  (build-sandbox-paths nix-configuration-build-sandbox-paths ;list of strings
+                       (default '()))
+  (extra-config        nix-configuration-extra-options ;list of strings
+                       (default '())))
 
 ;; Copied from gnu/services/base.scm
 (define* (nix-build-accounts count #:key
@@ -74,32 +83,49 @@ GID."
          (id 40000))
         (nix-build-accounts 10 #:group "nixbld")))
 
-(define (nix-activation _)
-  "Return the activation gexp."
-  (with-imported-modules '((guix build utils))
-    #~(begin
-        (use-modules (guix build utils)
-                     (srfi srfi-26))
-        (for-each (cut mkdir-p <>) '("/nix/store" "/nix/var/log"
-                                     "/nix/var/nix/gcroots/per-user"
-                                     "/nix/var/nix/profiles/per-user"))
-        (chown "/nix/store"
-               (passwd:uid (getpw "root")) (group:gid (getpw "nixbld01")))
-        (chmod "/nix/store" #o775)
-        (for-each (cut chmod <> #o777) '("/nix/var/nix/profiles"
-                                         "/nix/var/nix/profiles/per-user")))))
+(define nix-activation
+  ;; Return the activation gexp.
+  (match-lambda
+    (($ <nix-configuration> package sandbox build-sandbox-paths extra-config)
+     (with-imported-modules (source-module-closure
+                             '((guix build store-copy)))
+       #~(begin
+           (use-modules (guix build utils)
+                        (ice-9 format)
+                        (srfi srfi-1)
+                        (srfi srfi-26))
+           (for-each (cut mkdir-p <>) '("/nix/store" "/nix/var/log"
+                                        "/nix/var/nix/gcroots/per-user"
+                                        "/nix/var/nix/profiles/per-user"))
+           (chown "/nix/store"
+                  (passwd:uid (getpw "root")) (group:gid (getpw "nixbld01")))
+           (chmod "/nix/store" #o775)
+           (for-each (cut chmod <> #o777) '("/nix/var/nix/profiles"
+                                            "/nix/var/nix/profiles/per-user"))
+           (mkdir-p "/etc/nix")
+           (with-output-to-file "/etc/nix/nix.conf"
+             (lambda _
+               (format #t "sandbox = ~a~%" (if #$sandbox "true" "false"))
+               (format #t "build-sandbox-paths = ~{~a ~}~%"
+                       (append (append-map (cut call-with-input-file <> read)
+                                           '#$(map references-file
+                                                   (list package)))
+                               '#$build-sandbox-paths))
+               (for-each (cut display <>) '#$extra-config))))))))
 
-(define (nix-shepherd-service _)
-  "Return a <shepherd-service> for Nix."
-  (list
-   (shepherd-service
-    (provision '(nix-daemon))
-    (documentation "Run nix-daemon.")
-    (requirement '())
-    (start #~(make-forkexec-constructor
-              (list (string-append #$nix "/bin/nix-daemon"))))
-    (respawn? #f)
-    (stop #~(make-kill-destructor)))))
+(define nix-shepherd-service
+  ;; Return a <shepherd-service> for Nix.
+  (match-lambda
+    (($ <nix-configuration> package _ ...)
+     (list
+      (shepherd-service
+       (provision '(nix-daemon))
+       (documentation "Run nix-daemon.")
+       (requirement '())
+       (start #~(make-forkexec-constructor
+                 (list (string-append #$package "/bin/nix-daemon"))))
+       (respawn? #f)
+       (stop #~(make-kill-destructor)))))))
 
 (define nix-service-type
   (service-type
@@ -108,7 +134,7 @@ GID."
     (list (service-extension shepherd-root-service-type nix-shepherd-service)
           (service-extension account-service-type nix-accounts)
           (service-extension activation-service-type nix-activation)))
-   (default-value '())
-   (description "Run the Nix daemon.")))
+   (description "Run the Nix daemon.")
+   (default-value (nix-configuration))))
 
 ;;; nix.scm ends here
diff --git a/gnu/tests/package-management.scm b/gnu/tests/package-management.scm
new file mode 100644
index 0000000000..dbb9df22df
--- /dev/null
+++ b/gnu/tests/package-management.scm
@@ -0,0 +1,131 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 Oleg Pykhalov <[email-masked]>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu tests package-management)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages package-management)
+  #:use-module (gnu services)
+  #:use-module (gnu services networking)
+  #:use-module (gnu services nix)
+  #:use-module (gnu system)
+  #:use-module (gnu system vm)
+  #:use-module (gnu tests)
+  #:use-module (guix gexp)
+  #:use-module (guix git-download)
+  #:use-module (guix packages)
+  #:export (%test-nix))
+
+;;; Commentary:
+;;;
+;;; This module provides a test definition for the nix-daemon
+;;;
+;;; Code:
+
+(define* (run-nix-test name test-os)
+  "Run tests in %NIX-OS Guix operating system, which has nix-daemon running."
+  (define os
+    (marionette-operating-system
+     test-os
+     #:imported-modules '((gnu services herd))))
+
+  (define vm
+    (virtual-machine
+     (operating-system os)
+     (port-forwardings '((8080 . 80)))
+     (memory-size 1024)))
+
+  (define test
+    (with-imported-modules '((gnu build marionette))
+      #~(begin
+          (use-modules (srfi srfi-11)
+                       (srfi srfi-64)
+                       (gnu build marionette)
+                       (web client)
+                       (web response))
+
+          (define marionette
+            (make-marionette (list #$vm)))
+
+          (mkdir #$output)
+          (chdir #$output)
+
+          (test-begin #$name)
+
+          ;; XXX: Shepherd reads the config file *before* binding its control
+          ;; socket, so /var/run/shepherd/socket might not exist yet when the
+          ;; 'marionette' service is started.
+          (test-assert "shepherd socket ready"
+            (marionette-eval
+             `(begin
+                (use-modules (gnu services herd))
+                (let loop ((i 10))
+                  (cond ((file-exists? (%shepherd-socket-file))
+                         #t)
+                        ((> i 0)
+                         (sleep 1)
+                         (loop (- i 1)))
+                        (else
+                         'failure))))
+             marionette))
+
+          (test-assert "Nix daemon running"
+            (marionette-eval
+             '(begin
+                ;; Wait for nix-daemon to be up and running.
+                (start-service 'nix-daemon)
+                (with-output-to-file "guix-test.nix"
+                  (lambda ()
+                    (display "\
+with import <nix/config.nix>;
+
+derivation {
+  system = builtins.currentSystem;
+  name = \"guix-test\";
+  builder = shell;
+  args = [\"-c\" \"mkdir $out\\necho FOO > $out/foo\"];
+  PATH = coreutils;
+}
+")))
+                (zero? (system* (string-append #$nix "/bin/nix-build")
+                                "--substituters" "" "--debug" "--no-out-link"
+                                "guix-test.nix")))
+             marionette))
+
+	  (test-end)
+
+          (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
+
+  (gexp->derivation (string-append name "-test") test))
+
+(define %nix-os
+  ;; Return operating system under test.
+  (let ((base-os
+         (simple-operating-system
+          (service nix-service-type)
+	  (service dhcp-client-service-type))))
+    (operating-system
+      (inherit base-os)
+      (packages (cons nix (operating-system-packages base-os))))))
+
+(define %test-nix
+  (system-test
+   (name "nix")
+   (description "Connect to a running nix-daemon")
+   (value (run-nix-test name %nix-os))))
+
+;;; package-management.scm ends here
-- 
2.27.0