1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2017 Ryan Moe <ryan.moe@gmail.com>
3 ;;; Copyright © 2018, 2020-2022 Ludovic Courtès <ludo@gnu.org>
4 ;;; Copyright © 2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
5 ;;; Copyright © 2021 Timotej Lazar <timotej.lazar@araneo.si>
7 ;;; This file is part of GNU Guix.
9 ;;; GNU Guix is free software; you can redistribute it and/or modify it
10 ;;; under the terms of the GNU General Public License as published by
11 ;;; the Free Software Foundation; either version 3 of the License, or (at
12 ;;; your option) any later version.
14 ;;; GNU Guix is distributed in the hope that it will be useful, but
15 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;;; GNU General Public License for more details.
19 ;;; You should have received a copy of the GNU General Public License
20 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22 (define-module (gnu services virtualization)
23 #:use-module (gnu bootloader)
24 #:use-module (gnu bootloader grub)
25 #:use-module (gnu image)
26 #:use-module (gnu packages admin)
27 #:use-module (gnu packages gdb)
28 #:use-module (gnu packages package-management)
29 #:use-module (gnu packages ssh)
30 #:use-module (gnu packages virtualization)
31 #:use-module (gnu services base)
32 #:use-module (gnu services configuration)
33 #:use-module (gnu services dbus)
34 #:use-module (gnu services shepherd)
35 #:use-module (gnu services ssh)
36 #:use-module (gnu services)
37 #:use-module (gnu system file-systems)
38 #:use-module (gnu system hurd)
39 #:use-module (gnu system image)
40 #:use-module (gnu system shadow)
41 #:use-module (gnu system)
42 #:use-module (guix derivations)
43 #:use-module (guix gexp)
44 #:use-module (guix modules)
45 #:use-module (guix monads)
46 #:use-module (guix packages)
47 #:use-module (guix records)
48 #:use-module (guix store)
49 #:use-module (guix utils)
51 #:use-module (srfi srfi-9)
52 #:use-module (srfi srfi-26)
53 #:use-module (rnrs bytevectors)
54 #:use-module (ice-9 match)
56 #:export (%hurd-vm-operating-system
58 hurd-vm-configuration?
59 hurd-vm-configuration-os
60 hurd-vm-configuration-qemu
61 hurd-vm-configuration-image
62 hurd-vm-configuration-disk-size
63 hurd-vm-configuration-memory-size
64 hurd-vm-configuration-options
65 hurd-vm-configuration-id
66 hurd-vm-configuration-net-options
67 hurd-vm-configuration-secrets
84 qemu-binfmt-configuration
85 qemu-binfmt-configuration?
86 qemu-binfmt-service-type
88 qemu-guest-agent-configuration
89 qemu-guest-agent-configuration?
90 qemu-guest-agent-service-type))
92 (define (uglify-field-name field-name)
93 (let ((str (symbol->string field-name)))
95 (string-split (string-delete #\? str) #\-)
98 (define (quote-val val)
99 (string-append "\"" val "\""))
101 (define (serialize-field field-name val)
102 (format #t "~a = ~a\n" (uglify-field-name field-name) val))
104 (define (serialize-string field-name val)
105 (serialize-field field-name (quote-val val)))
107 (define (serialize-boolean field-name val)
108 (serialize-field field-name (if val 1 0)))
110 (define (serialize-integer field-name val)
111 (serialize-field field-name val))
113 (define (build-opt-list val)
116 (string-join (map quote-val val) ",")
119 (define optional-list? list?)
120 (define optional-string? string?)
122 (define (serialize-list field-name val)
123 (serialize-field field-name (build-opt-list val)))
125 (define (serialize-optional-list field-name val)
127 (format #t "# ~a = []\n" (uglify-field-name field-name))
128 (serialize-list field-name val)))
130 (define (serialize-optional-string field-name val)
131 (if (string-null? val)
132 (format #t "# ~a = \"\"\n" (uglify-field-name field-name))
133 (serialize-string field-name val)))
135 (define-configuration libvirt-configuration
145 "Flag listening for secure TLS connections on the public TCP/IP port.
146 must set @code{listen} for this to have any effect.
148 It is necessary to setup a CA and issue server certificates before
149 using this capability.")
152 "Listen for unencrypted TCP connections on the public TCP/IP port.
153 must set @code{listen} for this to have any effect.
155 Using the TCP socket requires SASL authentication by default. Only
156 SASL mechanisms which support data encryption are allowed. This is
157 DIGEST_MD5 and GSSAPI (Kerberos5)")
160 "Port for accepting secure TLS connections This can be a port number,
164 "Port for accepting insecure TCP connections This can be a port number,
168 "IP address or hostname used for client connections.")
171 "Flag toggling mDNS advertisement of the libvirt service.
173 Alternatively can disable for all services on a host by
174 stopping the Avahi daemon.")
176 (string (string-append "Virtualization Host " (gethostname)))
177 "Default mDNS advertisement name. This must be unique on the
178 immediate broadcast network.")
181 "UNIX domain socket group ownership. This can be used to
182 allow a 'trusted' set of users access to management capabilities
183 without becoming root.")
186 "UNIX socket permissions for the R/O socket. This is used
187 for monitoring VM status only.")
190 "UNIX socket permissions for the R/W socket. Default allows
191 only root. If PolicyKit is enabled on the socket, the default
192 will change to allow everyone (eg, 0777)")
193 (unix-sock-admin-perms
195 "UNIX socket permissions for the admin socket. Default allows
196 only owner (root), do not change it unless you are sure to whom
197 you are exposing the access to.")
199 (string "/var/run/libvirt")
200 "The directory in which sockets will be found/created.")
203 "Authentication scheme for UNIX read-only sockets. By default
204 socket permissions allow anyone to connect")
207 "Authentication scheme for UNIX read-write sockets. By default
208 socket permissions only allow root. If PolicyKit support was compiled
209 into libvirt, the default will be to use 'polkit' auth.")
212 "Authentication scheme for TCP sockets. If you don't enable SASL,
213 then all TCP traffic is cleartext. Don't do this outside of a dev/test
217 "Authentication scheme for TLS sockets. TLS sockets already have
218 encryption provided by the TLS layer, and limited authentication is
219 done by certificates.
221 It is possible to make use of any SASL authentication mechanism as
222 well, by using 'sasl' for this option")
225 "API access control scheme.
227 By default an authenticated user is allowed access to all APIs. Access
228 drivers can place restrictions on this.")
231 "Server key file path. If set to an empty string, then no private key
235 "Server key file path. If set to an empty string, then no certificate
239 "Server key file path. If set to an empty string, then no CA certificate
243 "Certificate revocation list path. If set to an empty string, then no
247 "Disable verification of our own server certificates.
249 When libvirtd starts it performs some sanity checks against its own
253 "Disable verification of client certificates.
255 Client certificate verification is the primary authentication mechanism.
256 Any client which does not present a certificate signed by the CA
260 "Whitelist of allowed x509 Distinguished Name.")
261 (sasl-allowed-usernames
263 "Whitelist of allowed SASL usernames. The format for username
264 depends on the SASL authentication mechanism.")
267 "Override the compile time default TLS priority string. The
268 default is usually \"NORMAL\" unless overridden at build time.
269 Only set this is it is desired for libvirt to deviate from
270 the global default settings.")
273 "Maximum number of concurrent client connections to allow
274 over all sockets combined.")
277 "Maximum length of queue of connections waiting to be
278 accepted by the daemon. Note, that some protocols supporting
279 retransmission may obey this so that a later reattempt at
280 connection succeeds.")
281 (max-anonymous-clients
283 "Maximum length of queue of accepted but not yet authenticated
284 clients. Set this to zero to turn this feature off")
287 "Number of workers to start up initially.")
290 "Maximum number of worker threads.
292 If the number of active clients exceeds @code{min-workers},
293 then more threads are spawned, up to max_workers limit.
294 Typically you'd want max_workers to equal maximum number
295 of clients allowed.")
298 "Number of priority workers. If all workers from above
299 pool are stuck, some calls marked as high priority
300 (notably domainDestroy) can be executed in this pool.")
303 "Total global limit on concurrent RPC calls.")
306 "Limit on concurrent requests from a single client
307 connection. To avoid one client monopolizing the server
308 this should be a small fraction of the global max_requests
309 and max_workers parameter.")
312 "Same as @code{min-workers} but for the admin interface.")
315 "Same as @code{max-workers} but for the admin interface.")
318 "Same as @code{max-clients} but for the admin interface.")
319 (admin-max-queued-clients
321 "Same as @code{max-queued-clients} but for the admin interface.")
322 (admin-max-client-requests
324 "Same as @code{max-client-requests} but for the admin interface.")
327 "Logging level. 4 errors, 3 warnings, 2 information, 1 debug.")
329 (string "3:remote 4:event")
332 A filter allows selecting a different logging level for a given category
334 The format for a filter is one of:
341 where @code{name} is a string which is matched against the category
342 given in the @code{VIR_LOG_INIT()} at the top of each libvirt source
343 file, e.g., \"remote\", \"qemu\", or \"util.json\" (the name in the
344 filter can be a substring of the full category name, in order
345 to match multiple similar categories), the optional \"+\" prefix
346 tells libvirt to log stack trace for each message matching
347 name, and @code{x} is the minimal level where matching messages should
357 Multiple filters can be defined in a single filters statement, they just
358 need to be separated by spaces.")
360 (string "3:syslog:libvirtd")
363 An output is one of the places to save logging information
364 The format for an output can be:
368 output goes to stderr
371 use syslog for the output and use the given name as the ident
373 @item x:file:file_path
374 output to a file, with the given filepath
377 output to journald logging system
380 In all case the x prefix is the minimal level, acting as a filter
389 Multiple outputs can be defined, they just need to be separated by spaces.")
392 "Allows usage of the auditing subsystem to be altered
395 @item 0: disable all auditing
396 @item 1: enable auditing, only if enabled on host
397 @item 2: enable auditing, and exit if disabled on host.
402 "Send audit messages via libvirt logging infrastructure.")
405 "Host UUID. UUID must not have all digits be the same.")
408 "Source to read host UUID.
412 @item @code{smbios}: fetch the UUID from @code{dmidecode -s system-uuid}
414 @item @code{machine-id}: fetch the UUID from @code{/etc/machine-id}
418 If @code{dmidecode} does not provide a valid UUID a temporary UUID
422 "A keepalive message is sent to a client after
423 @code{keepalive_interval} seconds of inactivity to check if
424 the client is still responding. If set to -1, libvirtd will
425 never send keepalive requests; however clients can still send
426 them and the daemon will send responses.")
429 "Maximum number of keepalive messages that are allowed to be sent
430 to the client without getting any response before the connection is
433 In other words, the connection is automatically
434 closed approximately after
435 @code{keepalive_interval * (keepalive_count + 1)} seconds since the last
436 message received from the client. When @code{keepalive-count} is
437 set to 0, connections will be automatically closed after
438 @code{keepalive-interval} seconds of inactivity without sending any
439 keepalive messages.")
440 (admin-keepalive-interval
442 "Same as above but for admin interface.")
443 (admin-keepalive-count
445 "Same as above but for admin interface.")
448 "Timeout for Open vSwitch calls.
450 The @code{ovs-vsctl} utility is used for the configuration and
451 its timeout option is set by default to 5 seconds to avoid
452 potential infinite waits blocking libvirt."))
454 (define* (libvirt-conf-file config)
455 "Return a libvirtd config file."
456 (plain-file "libvirtd.conf"
457 (with-output-to-string
459 (serialize-configuration config libvirt-configuration-fields)))))
461 (define %libvirt-accounts
462 (list (user-group (name "libvirt") (system? #t))))
464 (define (%libvirt-activation config)
465 (let ((sock-dir (libvirt-configuration-unix-sock-dir config)))
467 (use-modules (guix build utils))
468 (mkdir-p #$sock-dir))))
471 (define (libvirt-shepherd-service config)
472 (let* ((config-file (libvirt-conf-file config))
473 (libvirt (libvirt-configuration-libvirt config)))
474 (list (shepherd-service
475 (documentation "Run the libvirt daemon.")
476 (provision '(libvirtd))
477 (start #~(make-forkexec-constructor
478 (list (string-append #$libvirt "/sbin/libvirtd")
480 ;; For finding qemu and ip binaries.
481 #:environment-variables
483 "PATH=/run/current-system/profile/bin:"
484 "/run/current-system/profile/sbin"))))
485 (stop #~(make-kill-destructor))))))
487 (define libvirt-service-type
488 (service-type (name 'libvirt)
491 (service-extension polkit-service-type
492 (compose list libvirt-configuration-libvirt))
493 (service-extension profile-service-type
496 (libvirt-configuration-libvirt config)
497 (libvirt-configuration-qemu config))))
498 (service-extension activation-service-type
500 (service-extension shepherd-root-service-type
501 libvirt-shepherd-service)
502 (service-extension account-service-type
503 (const %libvirt-accounts))))
504 (default-value (libvirt-configuration))
505 (description "Run @command{libvirtd}, a daemon of the libvirt
506 virtualization management system. This daemon runs on host servers and
507 performs required management tasks for virtualized guests.")))
510 (define-record-type* <virtlog-configuration>
511 virtlog-configuration make-virtlog-configuration
512 virtlog-configuration?
513 (libvirt virtlog-configuration-libvirt
515 (log-level virtlog-configuration-log-level
517 (log-filters virtlog-configuration-log-filters
518 (default "3:remote 4:event"))
519 (log-outputs virtlog-configuration-log-outputs
520 (default "3:syslog:virtlogd"))
521 (max-clients virtlog-configuration-max-clients
523 (max-size virtlog-configuration-max-size
524 (default 2097152)) ;; 2MB
525 (max-backups virtlog-configuration-max-backups
528 (define* (virtlogd-conf-file config)
529 "Return a virtlogd config file."
530 (plain-file "virtlogd.conf"
532 "log_level = " (number->string (virtlog-configuration-log-level config)) "\n"
533 "log_filters = \"" (virtlog-configuration-log-filters config) "\"\n"
534 "log_outputs = \"" (virtlog-configuration-log-outputs config) "\"\n"
535 "max_clients = " (number->string (virtlog-configuration-max-clients config)) "\n"
536 "max_size = " (number->string (virtlog-configuration-max-size config)) "\n"
537 "max_backups = " (number->string (virtlog-configuration-max-backups config)) "\n")))
539 (define (virtlogd-shepherd-service config)
540 (let* ((config-file (virtlogd-conf-file config))
541 (libvirt (virtlog-configuration-libvirt config)))
542 (list (shepherd-service
543 (documentation "Run the virtlog daemon.")
544 (provision '(virtlogd))
545 (start #~(make-forkexec-constructor
546 (list (string-append #$libvirt "/sbin/virtlogd")
547 "-f" #$config-file)))
548 (stop #~(make-kill-destructor))))))
550 (define virtlog-service-type
551 (service-type (name 'virtlogd)
554 (service-extension shepherd-root-service-type
555 virtlogd-shepherd-service)))
556 (default-value (virtlog-configuration))
557 (description "Run @command{virtlogd}, a daemon libvirt that is
558 used to manage logs from @acronym{VM, virtual machine} consoles.")))
560 (define (generate-libvirt-documentation)
561 (generate-documentation
562 `((libvirt-configuration ,libvirt-configuration-fields))
563 'libvirt-configuration))
567 ;;; Transparent QEMU emulation via binfmt_misc.
570 ;; Platforms that QEMU can emulate.
571 (define-record-type* <qemu-platform>
572 qemu-platform make-qemu-platform
574 (name qemu-platform-name) ;string
575 (family qemu-platform-family) ;string
576 (magic qemu-platform-magic) ;bytevector
577 (mask qemu-platform-mask) ;bytevector
581 ;; "F": fix binary. Open the qemu-user binary (statically linked) as soon
582 ;; as binfmt_misc interpretation is handled.
584 ;; "P": preserve argv[0]. QEMU 6.0 detects whether it's started with this
585 ;; flag and automatically does the right thing. Without this flag,
586 ;; argv[0] is replaced by the absolute file name of the executable, an
587 ;; observable difference that can cause discrepancies.
588 (flags qemu-platform-flags (default "FP"))) ;string
592 "Expand the given string into a bytevector."
595 (string? (syntax->datum #'str))
596 (let ((bv (u8-list->bytevector
598 (string->list (syntax->datum #'str))))))
601 ;;; The platform descriptions below are taken from
602 ;;; 'scripts/qemu-binfmt-conf.sh' in QEMU.
608 (magic (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00"))
609 (mask (bv "\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
615 (magic (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90"))
616 (mask (bv "\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
622 (magic (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00"))
623 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
629 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28"))
630 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
636 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02"))
637 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
643 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12"))
644 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
650 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14"))
651 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
657 (magic (bv "\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15"))
658 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
664 (magic (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00"))
665 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00"))))
671 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04"))
672 (mask (bv "\xff\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
674 ;; XXX: We could use the other endianness on a MIPS host.
679 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08"))
680 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
686 (magic (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00"))
687 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
693 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08"))
694 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
700 (magic (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00"))
701 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
707 (magic (bv "\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08"))
708 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
714 (magic (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00"))
715 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
721 (magic (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00"))
722 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
728 (magic (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00"))
729 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
735 (magic (bv "\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00"))
736 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
742 (magic (bv "\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a"))
743 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
749 (magic (bv "\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16"))
750 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
756 (magic (bv "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00"))
757 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"))))
763 (magic (bv "\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x0f"))
764 (mask (bv "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff"))))
766 (define %qemu-platforms
767 (list %i386 %alpha %arm %sparc32plus %ppc %ppc64 %ppc64le %m68k
768 %mips %mipsel %mipsn32 %mipsn32el %mips64 %mips64el
769 %riscv32 %riscv64 %sh4 %sh4eb %s390x %aarch64 %hppa))
771 (define (lookup-qemu-platforms . names)
772 "Return the list of QEMU platforms that match NAMES--a list of names such as
773 \"arm\", \"hppa\", etc."
774 (filter (lambda (platform)
775 (member (qemu-platform-name platform) names))
778 (define-record-type* <qemu-binfmt-configuration>
779 qemu-binfmt-configuration make-qemu-binfmt-configuration
780 qemu-binfmt-configuration?
781 (qemu qemu-binfmt-configuration-qemu
783 (platforms qemu-binfmt-configuration-platforms
784 (default '()))) ;safest default
786 (define (qemu-platform->binfmt qemu platform)
787 "Return a gexp that evaluates to a binfmt string for PLATFORM, using the
789 (define (bytevector->binfmt-string bv)
790 ;; Return a binfmt-friendly string representing BV. Hex-encode every
791 ;; character, in particular because the doc notes "that you must escape
792 ;; any NUL bytes; parsing halts at the first one".
796 (string-pad (number->string n 16) 2 #\0)))
797 (bytevector->u8-list bv))))
800 (($ <qemu-platform> name family magic mask flags)
801 ;; See 'Documentation/binfmt_misc.txt' in the kernel.
802 #~(string-append ":qemu-" #$name ":M::"
803 #$(bytevector->binfmt-string magic)
804 ":" #$(bytevector->binfmt-string mask)
805 ":" #$qemu:static "/bin/qemu-" #$name
808 (define %binfmt-mount-point
809 (file-system-mount-point %binary-format-file-system))
811 (define %binfmt-register-file
812 (string-append %binfmt-mount-point "/register"))
814 (define qemu-binfmt-shepherd-services
816 (($ <qemu-binfmt-configuration> qemu platforms)
817 (list (shepherd-service
818 (provision '(qemu-binfmt))
819 (documentation "Install binfmt_misc handlers for QEMU.")
820 (requirement '(file-system-/proc/sys/fs/binfmt_misc))
822 ;; Register the handlers for all of PLATFORMS.
823 (for-each (lambda (str)
824 (call-with-output-file
825 #$%binfmt-register-file
827 (display str port))))
829 #$@(map (cut qemu-platform->binfmt qemu
834 ;; Unregister the handlers.
835 (for-each (lambda (name)
836 (let ((file (string-append
837 #$%binfmt-mount-point
839 (call-with-output-file file
841 (display "-1" port)))))
842 '#$(map qemu-platform-name platforms))
845 (define qemu-binfmt-service-type
846 ;; TODO: Make a separate binfmt_misc service out of this?
847 (service-type (name 'qemu-binfmt)
849 (list (service-extension file-system-service-type
851 (list %binary-format-file-system)))
852 (service-extension shepherd-root-service-type
853 qemu-binfmt-shepherd-services)))
854 (default-value (qemu-binfmt-configuration))
856 "This service supports transparent emulation of binaries
857 compiled for other architectures using QEMU and the @code{binfmt_misc}
858 functionality of the kernel Linux.")))
862 ;;; QEMU guest agent service.
865 (define-configuration qemu-guest-agent-configuration
867 (file-like qemu-minimal)
871 "Path to device or socket used to communicate with the host. If not
872 specified, the QEMU default path is used."))
874 (define (qemu-guest-agent-shepherd-service config)
875 (let ((qemu (qemu-guest-agent-configuration-qemu config))
876 (device (qemu-guest-agent-configuration-device config)))
879 (provision '(qemu-guest-agent))
880 (documentation "Run the QEMU guest agent.")
881 (start #~(make-forkexec-constructor
882 `(,(string-append #$qemu "/bin/qemu-ga")
883 "--statedir" "/var/run"
884 ,@(if (string-null? #$device)
886 (list "--path" #$device)))
887 #:log-file "/var/log/qemu-ga.log"))
888 (stop #~(make-kill-destructor))))))
890 (define qemu-guest-agent-service-type
892 (name 'qemu-guest-agent)
894 (list (service-extension shepherd-root-service-type
895 qemu-guest-agent-shepherd-service)))
896 (default-value (qemu-guest-agent-configuration))
897 (description "Run the QEMU guest agent.")))
901 ;;; Secrets for guest VMs.
904 (define (secret-service-shepherd-services port)
905 "Return a Shepherd service that fetches sensitive material at local PORT,
906 over TCP. Reboot upon failure."
907 ;; This is a Shepherd service, rather than an activation snippet, to make
908 ;; sure it is started once 'networking' is up so it can accept incoming
912 (documentation "Fetch secrets from the host at startup time.")
913 (provision '(secret-service-client))
914 (requirement '(loopback networking))
915 (modules '((gnu build secret-service)
917 (start (with-imported-modules '((gnu build secret-service)
920 ;; Since shepherd's output port goes to /dev/log, write this
921 ;; message to stderr so it's visible on the Mach console.
922 (format (current-error-port)
923 "receiving secrets from the host...~%")
924 (force-output (current-error-port))
926 (let ((sent (secret-service-receive-secrets #$port)))
930 (stop #~(const #f)))))
932 (define secret-service-type
934 (name 'secret-service)
935 (extensions (list (service-extension shepherd-root-service-type
936 secret-service-shepherd-services)
938 ;; Make every Shepherd service depend on
939 ;; 'secret-service-client'.
940 (service-extension user-processes-service-type
941 (const '(secret-service-client)))))
943 "This service fetches secret key and other sensitive material over TCP at
944 boot time. This service is meant to be used by virtual machines (VMs) that
945 can only be accessed by their host.")))
947 (define (secret-service-operating-system os)
948 "Return an operating system based on OS that includes the secret-service,
949 that will be listening to receive secret keys on port 1004, TCP."
953 ;; Turn off SSH and Guix key generation that normally happens during
954 ;; activation: that requires entropy and thus takes time during boot, and
955 ;; those keys are going to be overwritten by secrets received from the
957 (cons (service secret-service-type 1004)
958 (modify-services (operating-system-user-services os)
959 (openssh-service-type
960 config => (openssh-configuration
962 (generate-host-keys? #f)))
964 config => (guix-configuration
965 (generate-substitute-key? #f))))))))
969 ;;; The Hurd in VM service: a Childhurd.
972 (define %hurd-vm-operating-system
974 (inherit %hurd-default-operating-system)
975 (host-name "childhurd")
976 (timezone "Europe/Amsterdam")
977 (bootloader (bootloader-configuration
978 (bootloader grub-minimal-bootloader)
979 (targets '("/dev/vda"))
981 (packages (cons* gdb-minimal
982 (operating-system-packages
983 %hurd-default-operating-system)))
985 (service openssh-service-type
986 (openssh-configuration
987 (openssh openssh-sans-x)
990 (permit-root-login #t)
991 (allow-empty-passwords? #t)
992 (password-authentication? #t)))
994 ;; By default, the secret service introduces a pre-initialized
995 ;; /etc/guix/acl file in the childhurd. Thus, clear
996 ;; 'authorize-key?' so that it's not overridden at activation
998 (modify-services %base-services/hurd
999 (guix-service-type config =>
1002 (authorize-key? #f))))))))
1004 (define-record-type* <hurd-vm-configuration>
1005 hurd-vm-configuration make-hurd-vm-configuration
1006 hurd-vm-configuration?
1007 (os hurd-vm-configuration-os ;<operating-system>
1008 (default %hurd-vm-operating-system))
1009 (qemu hurd-vm-configuration-qemu ;file-like
1010 (default qemu-minimal))
1011 (image hurd-vm-configuration-image ;string
1013 (default (hurd-vm-disk-image this-record)))
1014 (disk-size hurd-vm-configuration-disk-size ;number or 'guess
1016 (memory-size hurd-vm-configuration-memory-size ;number
1018 (options hurd-vm-configuration-options ;list of string
1019 (default `("--snapshot")))
1020 (id hurd-vm-configuration-id ;#f or integer [1..]
1022 (net-options hurd-vm-configuration-net-options ;list of string
1024 (default (hurd-vm-net-options this-record)))
1025 (secret-root hurd-vm-configuration-secret-root ;string
1026 (default "/etc/childhurd")))
1028 (define (hurd-vm-disk-image config)
1029 "Return a disk-image for the Hurd according to CONFIG. The secret-service
1030 is added to the OS specified in CONFIG."
1031 (let* ((os (secret-service-operating-system
1032 (hurd-vm-configuration-os config)))
1033 (disk-size (hurd-vm-configuration-disk-size config))
1034 (type (lookup-image-type-by-name 'hurd-qcow2))
1035 (os->image (image-type-constructor type)))
1037 (image (inherit (os->image os))
1038 (size disk-size)))))
1040 (define (hurd-vm-port config base)
1041 "Return the forwarded vm port for this childhurd config."
1042 (let ((id (or (hurd-vm-configuration-id config) 0)))
1043 (+ base (* 1000 id))))
1044 (define %hurd-vm-secrets-port 11004)
1045 (define %hurd-vm-ssh-port 10022)
1046 (define %hurd-vm-vnc-port 15900)
1048 (define (hurd-vm-net-options config)
1049 `("--device" "rtl8139,netdev=net0"
1051 ,(string-append "user,id=net0"
1052 ",hostfwd=tcp:127.0.0.1:"
1053 (number->string (hurd-vm-port config %hurd-vm-secrets-port))
1055 ",hostfwd=tcp:127.0.0.1:"
1056 (number->string (hurd-vm-port config %hurd-vm-ssh-port))
1058 ",hostfwd=tcp:127.0.0.1:"
1059 (number->string (hurd-vm-port config %hurd-vm-vnc-port))
1062 (define (hurd-vm-shepherd-service config)
1063 "Return a <shepherd-service> for a Hurd in a Virtual Machine with CONFIG."
1065 (let ((image (hurd-vm-configuration-image config))
1066 (qemu (hurd-vm-configuration-qemu config))
1067 (memory-size (hurd-vm-configuration-memory-size config))
1068 (options (hurd-vm-configuration-options config))
1069 (id (hurd-vm-configuration-id config))
1070 (net-options (hurd-vm-configuration-net-options config))
1071 (provisions '(hurd-vm childhurd)))
1074 #~(append (list #$(file-append qemu "/bin/qemu-system-i386")
1075 "-m" (number->string #$memory-size)
1080 ;; Cause the service to be respawned if the guest
1081 ;; reboots (it can reboot for instance if it did not
1082 ;; receive valid secrets, or if it crashed.)
1084 (if (file-exists? "/dev/kvm")
1090 (documentation "Run the Hurd in a Virtual Machine: a Childhurd.")
1093 (cute symbol-append <>
1094 (string->symbol (number->string id)))
1097 (requirement '(loopback networking user-processes))
1099 (with-imported-modules
1100 (source-module-closure '((gnu build secret-service)
1101 (guix build utils)))
1103 (let ((pid (fork+exec-command #$vm-command
1105 ;; XXX TODO: use "childhurd" after
1106 ;; updating Shepherd
1108 #:environment-variables
1109 ;; QEMU tries to write to /var/tmp
1112 (port #$(hurd-vm-port config %hurd-vm-secrets-port))
1113 (root #$(hurd-vm-configuration-secret-root config)))
1116 ;; XXX: 'secret-service-send-secrets' won't complete until
1117 ;; the guest has booted and its secret service server is
1118 ;; running, which could take 20+ seconds during which PID 1
1119 ;; is stuck waiting.
1120 (if (secret-service-send-secrets port root)
1123 (kill (- pid) SIGTERM)
1125 (lambda (key . args)
1126 (kill (- pid) SIGTERM)
1127 (apply throw key args)))))))
1128 (modules `((gnu build secret-service)
1130 ,@%default-modules))
1131 (stop #~(make-kill-destructor))))))
1133 (define %hurd-vm-accounts
1134 (list (user-group (name "childhurd") (system? #t))
1138 (supplementary-groups '("kvm"))
1139 (comment "Privilege separation user for the childhurd")
1140 (home-directory "/var/empty")
1141 (shell (file-append shadow "/sbin/nologin"))
1144 (define (initialize-hurd-vm-substitutes)
1145 "Initialize the Hurd VM's key pair and ACL and store it on the host."
1147 (with-imported-modules '((guix build utils))
1149 (use-modules (guix build utils)
1153 "/etc/guix/signing-key.pub")
1158 (match (command-line)
1159 ((_ guest-config-directory)
1160 (setenv "GUIX_CONFIGURATION_DIRECTORY"
1161 guest-config-directory)
1162 (invoke #+(file-append guix "/bin/guix") "archive"
1165 (when (file-exists? host-acl)
1166 ;; Copy the host ACL.
1168 (string-append guest-config-directory
1171 (when (file-exists? host-key)
1172 ;; Add the host key to the childhurd's ACL.
1173 (let ((key (open-fdes host-key O_RDONLY)))
1176 (execl #+(file-append guix "/bin/guix")
1177 "guix" "archive" "--authorize"))))))))
1179 (program-file "initialize-hurd-vm-substitutes" run))
1181 (define (hurd-vm-activation config)
1182 "Return a gexp to activate the Hurd VM according to CONFIG."
1183 (with-imported-modules '((guix build utils))
1185 (use-modules (guix build utils))
1187 (define secret-directory
1188 #$(hurd-vm-configuration-secret-root config))
1190 (define ssh-directory
1191 (string-append secret-directory "/etc/ssh"))
1193 (define guix-directory
1194 (string-append secret-directory "/etc/guix"))
1196 (unless (file-exists? ssh-directory)
1197 ;; Generate SSH host keys under SSH-DIRECTORY.
1198 (mkdir-p ssh-directory)
1199 (invoke #$(file-append openssh "/bin/ssh-keygen")
1200 "-A" "-f" secret-directory))
1202 (unless (file-exists? guix-directory)
1203 (invoke #$(initialize-hurd-vm-substitutes)
1206 (define hurd-vm-service-type
1209 (extensions (list (service-extension shepherd-root-service-type
1210 hurd-vm-shepherd-service)
1211 (service-extension account-service-type
1212 (const %hurd-vm-accounts))
1213 (service-extension activation-service-type
1214 hurd-vm-activation)))
1215 (default-value (hurd-vm-configuration))
1217 "Provide a virtual machine (VM) running GNU/Hurd, also known as a
1218 @dfn{childhurd}.")))