services: hurd-vm: Implement zero-configuration offloading.

This allows for zero-configuration offloading to a childhurd.

* gnu/services/virtualization.scm (operating-system-with-offloading-account):
New procedure.
(<hurd-vm-configuration>)[offloading?]: New field.
(hurd-vm-disk-image): Define ‘transform’ and use it.
(hurd-vm-activation): Generate SSH key for user ‘offloading’ and add
authorize it via /etc/childhurd/etc/ssh/authorized_keys.d.
(hurd-vm-configuration-offloading-ssh-key)
(hurd-vm-guix-extension): New procedures.
(hurd-vm-service-type): Add GUIX-SERVICE-TYPE extension.
* gnu/tests/virtualization.scm (run-childhurd-test)[import-module?]: New
procedure.
[os]: Add (gnu build install) and its closure to #:import-modules.
[test]: Add “copy-on-write store” and “offloading” tests.
* doc/guix.texi (Virtualization Services): Document it.
This commit is contained in:
Ludovic Courtès 2023-09-21 16:38:22 +02:00
parent 990d20d4a8
commit 953c65ffdd
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
3 changed files with 169 additions and 32 deletions

View file

@ -35722,6 +35722,15 @@ guix shell tigervnc-client -- vncviewer localhost:5900
The default configuration (see @code{hurd-vm-configuration} below) The default configuration (see @code{hurd-vm-configuration} below)
spawns a secure shell (SSH) server in your GNU/Hurd system, which QEMU spawns a secure shell (SSH) server in your GNU/Hurd system, which QEMU
(the virtual machine emulator) redirects to port 10222 on the host. (the virtual machine emulator) redirects to port 10222 on the host.
By default, the service enables @dfn{offloading} such that the host
@code{guix-daemon} automatically offloads GNU/Hurd builds to the
childhurd (@pxref{Daemon Offload Setup}). This is what happens when
running a command like the following one, where @code{i586-gnu} is the
system type of 32-bit GNU/Hurd:
@example
guix build emacs-minimal -s i586-gnu
@end example
The childhurd is volatile and stateless: it starts with a fresh root The childhurd is volatile and stateless: it starts with a fresh root
file system every time you restart it. By default though, all the files file system every time you restart it. By default though, all the files
@ -35855,6 +35864,41 @@ with forwarded ports:
@var{vnc-port}: @code{(+ 15900 (* 1000 @var{ID}))} @var{vnc-port}: @code{(+ 15900 (* 1000 @var{ID}))}
@end example @end example
@cindex childhurd, offloading
@cindex Hurd, offloading
@item @code{offloading?} (default: @code{#t})
Whether to automatically set up offloading of builds to the childhurd.
When enabled, this lets you run GNU/Hurd builds on the host and have
them transparently offloaded to the VM, for instance when running a
command like this:
@example
guix build coreutils -s i586-gnu
@end example
This option automatically sets up offloading like so:
@enumerate
@item
Authorizing the childhurd's key on the host so that the host accepts
build results coming from the childhurd, which can be done like so
(@pxref{Invoking guix archive, @command{guix archive --authorize}}, for
more on that).
@item
Creating a user account called @code{offloading} dedicated to offloading
in the childhurd.
@item
Creating an SSH key pair on the host and making it an authorized key of
the @code{offloading} account in the childhurd.
@item
Adding the childhurd to @file{/etc/guix/machines.scm} (@pxref{Daemon
Offload Setup}).
@end enumerate
@item @code{secret-root} (default: @file{/etc/childhurd}) @item @code{secret-root} (default: @file{/etc/childhurd})
The root directory with out-of-band secrets to be installed into the The root directory with out-of-band secrets to be installed into the
childhurd once it runs. Childhurds are volatile which means that on childhurd once it runs. Childhurds are volatile which means that on
@ -35872,38 +35916,13 @@ with the following non-volatile secrets, unless they already exist:
/etc/childhurd/etc/guix/acl /etc/childhurd/etc/guix/acl
/etc/childhurd/etc/guix/signing-key.pub /etc/childhurd/etc/guix/signing-key.pub
/etc/childhurd/etc/guix/signing-key.sec /etc/childhurd/etc/guix/signing-key.sec
/etc/childhurd/etc/ssh/authorized_keys.d/offloading
/etc/childhurd/etc/ssh/ssh_host_ed25519_key /etc/childhurd/etc/ssh/ssh_host_ed25519_key
/etc/childhurd/etc/ssh/ssh_host_ecdsa_key /etc/childhurd/etc/ssh/ssh_host_ecdsa_key
/etc/childhurd/etc/ssh/ssh_host_ed25519_key.pub /etc/childhurd/etc/ssh/ssh_host_ed25519_key.pub
/etc/childhurd/etc/ssh/ssh_host_ecdsa_key.pub /etc/childhurd/etc/ssh/ssh_host_ecdsa_key.pub
@end example @end example
These files are automatically sent to the guest Hurd VM when it boots,
including permissions.
@cindex childhurd, offloading
@cindex Hurd, offloading
Having these files in place means that only a couple of things are
missing to allow the host to offload @code{i586-gnu} builds to the
childhurd:
@enumerate
@item
Authorizing the childhurd's key on the host so that the host accepts
build results coming from the childhurd, which can be done like so:
@example
guix archive --authorize < \
/etc/childhurd/etc/guix/signing-key.pub
@end example
@item
Adding the childhurd to @file{/etc/guix/machines.scm} (@pxref{Daemon
Offload Setup}).
@end enumerate
We're working towards making that happen automatically---get in touch
with us at @email{guix-devel@@gnu.org} to discuss it!
@end table @end table
@end deftp @end deftp

View file

@ -27,6 +27,7 @@
#:use-module (gnu bootloader grub) #:use-module (gnu bootloader grub)
#:use-module (gnu image) #:use-module (gnu image)
#:use-module (gnu packages admin) #:use-module (gnu packages admin)
#:use-module (gnu packages bash)
#:use-module (gnu packages gdb) #:use-module (gnu packages gdb)
#:autoload (gnu packages gnupg) (guile-gcrypt) #:autoload (gnu packages gnupg) (guile-gcrypt)
#:use-module (gnu packages package-management) #:use-module (gnu packages package-management)
@ -52,6 +53,7 @@
#:use-module (guix store) #:use-module (guix store)
#:use-module (guix utils) #:use-module (guix utils)
#:autoload (guix self) (make-config.scm) #:autoload (guix self) (make-config.scm)
#:autoload (guix platform) (platform-system)
#:use-module (srfi srfi-9) #:use-module (srfi srfi-9)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
@ -1063,6 +1065,26 @@ that will be listening to receive secret keys on port 1004, TCP."
;;; The Hurd in VM service: a Childhurd. ;;; The Hurd in VM service: a Childhurd.
;;; ;;;
(define (operating-system-with-offloading-account os)
(define accounts
(list (user-group
(name "offloading")
(system? #t))
(user-account
(name "offloading")
(group "offloading")
(system? #t)
(comment "Offloading privilege separation user")
(home-directory "/var/run/offloading")
(shell (file-append bash-minimal "/bin/sh")))))
(operating-system
(inherit os)
(services (cons (simple-service 'offloading-account
account-service-type
accounts)
(operating-system-user-services os)))))
(define %hurd-vm-operating-system (define %hurd-vm-operating-system
(operating-system (operating-system
(inherit %hurd-default-operating-system) (inherit %hurd-default-operating-system)
@ -1115,14 +1137,21 @@ that will be listening to receive secret keys on port 1004, TCP."
(net-options hurd-vm-configuration-net-options ;list of string (net-options hurd-vm-configuration-net-options ;list of string
(thunked) (thunked)
(default (hurd-vm-net-options this-record))) (default (hurd-vm-net-options this-record)))
(offloading? hurd-vm-configuration-offloading? ;Boolean
(default #t))
(secret-root hurd-vm-configuration-secret-root ;string (secret-root hurd-vm-configuration-secret-root ;string
(default "/etc/childhurd"))) (default "/etc/childhurd")))
(define (hurd-vm-disk-image config) (define (hurd-vm-disk-image config)
"Return a disk-image for the Hurd according to CONFIG. The secret-service "Return a disk-image for the Hurd according to CONFIG. The secret-service
is added to the OS specified in CONFIG." is added to the OS specified in CONFIG."
(let* ((os (secret-service-operating-system (define transform
(hurd-vm-configuration-os config))) (compose secret-service-operating-system
(if (hurd-vm-configuration-offloading? config)
operating-system-with-offloading-account
identity)))
(let* ((os (transform (hurd-vm-configuration-os config)))
(disk-size (hurd-vm-configuration-disk-size config)) (disk-size (hurd-vm-configuration-disk-size config))
(type (lookup-image-type-by-name 'hurd-qcow2)) (type (lookup-image-type-by-name 'hurd-qcow2))
(os->image (image-type-constructor type))) (os->image (image-type-constructor type)))
@ -1331,18 +1360,71 @@ an argument) on the host."
(define guix-directory (define guix-directory
(string-append secret-directory "/etc/guix")) (string-append secret-directory "/etc/guix"))
(define offloading-ssh-key
#$(hurd-vm-configuration-offloading-ssh-key config))
(unless (file-exists? ssh-directory) (unless (file-exists? ssh-directory)
;; Generate SSH host keys under SSH-DIRECTORY. ;; Generate SSH host keys under SSH-DIRECTORY.
(mkdir-p ssh-directory) (mkdir-p ssh-directory)
(invoke #$(file-append openssh "/bin/ssh-keygen") (invoke #$(file-append openssh "/bin/ssh-keygen")
"-A" "-f" secret-directory)) "-A" "-f" secret-directory))
(unless (or (not #$(hurd-vm-configuration-offloading? config))
(file-exists? offloading-ssh-key))
;; Generate a user SSH key pair for the host to use when offloading
;; to the guest.
(mkdir-p (dirname offloading-ssh-key))
(invoke #$(file-append openssh "/bin/ssh-keygen")
"-t" "ed25519" "-N" ""
"-f" offloading-ssh-key)
;; Authorize it in the guest for user 'offloading'.
(let ((authorizations
(string-append ssh-directory
"/authorized_keys.d/offloading")))
(mkdir-p (dirname authorizations))
(copy-file (string-append offloading-ssh-key ".pub")
authorizations)
(chmod (dirname authorizations) #o555)))
(unless (file-exists? guix-directory) (unless (file-exists? guix-directory)
(invoke #$(initialize-hurd-vm-substitutes) (invoke #$(initialize-hurd-vm-substitutes)
guix-directory)) guix-directory))
(when #$(hurd-vm-configuration-offloading? config)
;; Authorize the archive signing key from GUIX-DIRECTORY in the host. ;; Authorize the archive signing key from GUIX-DIRECTORY in the host.
(invoke #$(authorize-guest-substitutes-on-host) guix-directory)))) (invoke #$(authorize-guest-substitutes-on-host) guix-directory)))))
(define (hurd-vm-configuration-offloading-ssh-key config)
"Return the name of the file containing the SSH key of user 'offloading'."
(string-append "/etc/guix/offload/ssh/childhurd"
(or (and=> (hurd-vm-configuration-id config)
number->string)
"")))
(define (hurd-vm-guix-extension config)
"When offloading is enabled, add this childhurd to the list of offlading
machines in /etc/guix/machines.scm."
(if (hurd-vm-configuration-offloading? config)
(let* ((image (hurd-vm-configuration-image config))
(platform (image-platform image))
(system (platform-system platform))
(vm-ssh-key (string-append
(hurd-vm-configuration-secret-root config)
"/etc/ssh/ssh_host_ed25519_key.pub"))
(host-ssh-key (hurd-vm-configuration-offloading-ssh-key config)))
(guix-extension
(build-machines
(list #~(build-machine
(name "localhost")
(port #$(hurd-vm-port config %hurd-vm-ssh-port))
(systems '(#$system))
(host-key (call-with-input-file #$vm-ssh-key
(@ (ice-9 textual-ports)
get-string-all)))
(user "offloading")
(private-key #$host-ssh-key))))))
(guix-extension)))
(define hurd-vm-service-type (define hurd-vm-service-type
(service-type (service-type
@ -1351,6 +1433,8 @@ an argument) on the host."
hurd-vm-shepherd-service) hurd-vm-shepherd-service)
(service-extension account-service-type (service-extension account-service-type
(const %hurd-vm-accounts)) (const %hurd-vm-accounts))
(service-extension guix-service-type
hurd-vm-guix-extension)
(service-extension activation-service-type (service-extension activation-service-type
hurd-vm-activation))) hurd-vm-activation)))
(default-value (hurd-vm-configuration)) (default-value (hurd-vm-configuration))

View file

@ -38,6 +38,7 @@
#:use-module (guix gexp) #:use-module (guix gexp)
#:use-module (guix records) #:use-module (guix records)
#:use-module (guix store) #:use-module (guix store)
#:use-module (guix modules)
#:export (%test-libvirt #:export (%test-libvirt
%test-qemu-guest-agent %test-qemu-guest-agent
%test-childhurd)) %test-childhurd))
@ -244,11 +245,19 @@
(permit-root-login #t))))))))))) (permit-root-login #t)))))))))))
(define (run-childhurd-test) (define (run-childhurd-test)
(define (import-module? module)
;; This module is optional and depends on Guile-Gcrypt, do skip it.
(and (guix-module-name? module)
(not (equal? module '(guix store deduplication)))))
(define os (define os
(marionette-operating-system (marionette-operating-system
%childhurd-os %childhurd-os
#:imported-modules '((gnu services herd) #:imported-modules (source-module-closure
(guix combinators)))) '((gnu services herd)
(guix combinators)
(gnu build install))
#:select? import-module?)))
(define vm (define vm
(virtual-machine (virtual-machine
@ -373,6 +382,31 @@
(pk 'drv (string-trim-right drv))) (pk 'drv (string-trim-right drv)))
drv))) drv)))
(test-assert "copy-on-write store"
;; Set up a writable store. The root partition is already an
;; overlayfs, which is not suitable as the bottom part of this
;; additional overlayfs; thus, create a tmpfs for the backing
;; store.
;; TODO: Remove this when <virtual-machine> creates a writable
;; store.
(marionette-eval
'(begin
(use-modules (gnu build install)
(guix build syscalls))
(mkdir "/run/writable-store")
(mount "none" "/run/writable-store" "tmpfs")
(mount-cow-store "/run/writable-store" "/backing-store")
(system* "df" "-hT"))
marionette))
(test-equal "offloading"
0
(marionette-eval
'(and (file-exists? "/etc/guix/machines.scm")
(system* "guix" "offload" "test"))
marionette))
(test-end)))) (test-end))))
(gexp->derivation "childhurd-test" test)) (gexp->derivation "childhurd-test" test))