Subject: Re: bug#23605: /dev/urandom not seeded across reboots

From: Leo Famulari <[email-masked]>
Date: Sat, 28 May 2016 14:05:35 -0400

On Sat, May 28, 2016 at 03:57:06PM +0200, Ludovic Courtès wrote:
> Leo Famulari <[email-masked]> skribis:
> > On Wed, May 25, 2016 at 06:54:58PM +0200, Ludovic Courtès wrote:
> Usually, the service’s value is a configuration object, but in this
> case, the service’s value doesn’t matter, so you could simply write:
> 
>   (define (urandom-seed-shepherd-service _)
>     …)
> 
> > +                       ;; Add urandom-seed to the system profile
> > +                       (service-extension profile-service-type list)))))
> 
> The ‘profile-service-type’ represents the system profile, i.e.,
> /run/current-system/profile.  Extending it means adding a package to
> it.
> 
> But here, IIUC, there’s no package to be added to the profile, so you
> should just remove it.
> 
> Last round and we’re done!  :-)

Please find my latest patch attached. It seems to work for me!
>From 18979451b1af7eebaa354c1753ad4c90af288589 Mon Sep 17 00:00:00 2001
From: Leo Famulari <[email-masked]>
Date: Sat, 28 May 2016 13:41:21 -0400
Subject: [PATCH] services: Add urandom-seed-service.

* gnu/services/base.scm (urandom-seed-service): New procedure.
(%random-seed-file, urandom-seed-service-type): New variables.
(%urandom-seed-shepherd-service): New procedure.
* doc/guix.texi (Base Services): Document it.
---
 doc/guix.texi         | 10 +++++++++
 gnu/services/base.scm | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index bb75425..34a51a8 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -7355,6 +7355,16 @@ Return a service that runs the Guix build daemon according to
 Run @var{udev}, which populates the @file{/dev} directory dynamically.
 @end deffn
 
+@deffn {Scheme Procedure} urandom-seed-service @var{#f}
+Save some entropy in @var{%random-seed-file} to seed @file{/dev/urandom}
+when rebooting.
+@end deffn
+
+@deftp {Data Type} %random-seed-file
+This is where some random bytes are saved by @var{urandom-seed-service}
+to seed @file{/dev/urandom} when rebooting.
+@end deftp
+
 @deffn {Scheme Procedure} console-keymap-service @var{files} ...
 @cindex keyboard layout
 Return a service to load console keymaps from @var{files} using
diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index 96bf8da..032f713 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -93,6 +93,8 @@
             gpm-service-type
             gpm-service
 
+            urandom-seed-service
+
             %base-services))
 
 ;;; Commentary:
@@ -422,6 +424,63 @@ stopped before 'kill' is called."
 
 
 ;;;
+;;; Preserve entropy to seed /dev/urandom on boot.
+;;;
+
+(define %random-seed-file
+  "/var/lib/random-seed")
+
+(define %urandom-seed-activation
+  ;; Activation gexp for the urandom seed
+  #~(begin
+      (use-modules (guix build utils))
+
+      (mkdir-p (dirname #$%random-seed-file))
+      (close-port (open-file #$%random-seed-file "a0b"))
+      (chmod #$%random-seed-file #o600)))
+
+(define (urandom-seed-shepherd-service _)
+  "Return a shepherd service for the /dev/urandom seed."
+  (list (shepherd-service
+         (documentation "Preserve entropy across reboots for /dev/urandom.")
+         (provision '(urandom-seed))
+         (requirement '(user-processes))
+         (start #~(lambda _
+                    ;; On boot, write random seed into /dev/urandom.
+                    (when (file-exists? #$%random-seed-file)
+                      (call-with-input-file #$%random-seed-file
+                        (lambda (seed)
+                          (call-with-output-file "/dev/urandom"
+                            (lambda (urandom)
+                              (dump-port seed urandom))))))
+                    #t))
+         (stop #~(lambda _
+                   ;; During shutdown, write from /dev/urandom into random seed.
+                   (let ((buf (make-bytevector 512)))
+                     (call-with-input-file "/dev/urandom"
+                       (lambda (urandom)
+                         (get-bytevector-n! urandom buf 0 512)
+                         (call-with-output-file #$%random-seed-file
+                           (lambda (seed)
+                             (put-bytevector seed buf)))
+                         #t)))))
+         (modules `((rnrs bytevectors)
+                    (rnrs io ports)
+                    ,@%default-modules)))))
+
+(define urandom-seed-service-type
+  (service-type (name 'urandom-seed)
+                (extensions
+                 (list (service-extension shepherd-root-service-type
+                                          urandom-seed-shepherd-service)
+                       (service-extension activation-service-type
+                                          (const %urandom-seed-activation))))))
+
+(define (urandom-seed-service)
+  (service urandom-seed-service-type #f))
+
+
+;;;
 ;;; System-wide environment variables.
 ;;;
 
@@ -1200,7 +1259,6 @@ extra rules from the packages listed in @var{rules}."
   "Return a service that uses @var{device} as a swap device."
   (service swap-service-type device))
 
-
 (define-record-type* <gpm-configuration>
   gpm-configuration make-gpm-configuration gpm-configuration?
   (gpm      gpm-configuration-gpm)                ;package
@@ -1281,6 +1339,7 @@ This is the GNU operating system, welcome!\n\n")))
           (static-networking-service "lo" "127.0.0.1"
                                      #:provision '(loopback))
           (syslog-service)
+          (urandom-seed-service)
           (guix-service)
           (nscd-service)
 
-- 
2.8.3