diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2024-01-20 14:55:46 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2024-02-10 23:21:07 +0100 |
| commit | 9edbb2d7a40c9da7583a1046e39b87633459f656 (patch) | |
| tree | e056280c955c0ab5e09fa3e3e0d1f6a1000458e3 /gnu/services/virtualization.scm | |
| parent | 5f34796dc4a615c8fe496bbb9cc18a489bc5d107 (diff) | |
services: Add ‘virtual-build-machine’ service.
* gnu/services/virtualization.scm (<virtual-build-machine>): New record type.
(%build-vm-ssh-port, %build-vm-secrets-port, %x86-64-intel-cpu-models):
New variables.
(qemu-cpu-model-for-date, virtual-build-machine-ssh-port)
(virtual-build-machine-secrets-port): New procedures.
(%minimal-vm-syslog-config, %virtual-build-machine-operating-system):
New variables.
(virtual-build-machine-default-image):
(virtual-build-machine-account-name)
(virtual-build-machine-accounts)
(build-vm-shepherd-services)
(initialize-build-vm-substitutes)
(build-vm-activation)
(virtual-build-machine-offloading-ssh-key)
(virtual-build-machine-activation)
(virtual-build-machine-secret-root)
(check-vm-availability)
(build-vm-guix-extension): New procedures.
(initialize-hurd-vm-substitutes): Remove.
(hurd-vm-activation): Rewrite in terms of ‘build-vm-activation’.
* gnu/system/vm.scm (linux-image-startup-command): New procedure.
(operating-system-for-image): Export.
* gnu/tests/virtualization.scm (run-command-over-ssh): New procedure,
extracted from…
(run-childhurd-test): … here.
[test]: Adjust accordingly.
(%build-vm-os): New variable.
(run-build-vm-test): New procedure.
(%test-build-vm): New variable.
* doc/guix.texi (Virtualization Services)[Virtual Build Machines]: New
section.
(Build Environment Setup): Add cross-reference.
Change-Id: I0a47652a583062314020325aedb654f11cb2499c
Diffstat (limited to 'gnu/services/virtualization.scm')
| -rw-r--r-- | gnu/services/virtualization.scm | 602 |
1 files changed, 472 insertions, 130 deletions
diff --git a/gnu/services/virtualization.scm b/gnu/services/virtualization.scm index 5b8566f6009..cc95dfdf223 100644 --- a/gnu/services/virtualization.scm +++ b/gnu/services/virtualization.scm | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ;;; GNU Guix --- Functional package management for GNU | 1 | ;;; GNU Guix --- Functional package management for GNU |
| 2 | ;;; Copyright © 2017 Ryan Moe <ryan.moe@gmail.com> | 2 | ;;; Copyright © 2017 Ryan Moe <ryan.moe@gmail.com> |
| 3 | ;;; Copyright © 2018, 2020-2023 Ludovic Courtès <ludo@gnu.org> | 3 | ;;; Copyright © 2018, 2020-2024 Ludovic Courtès <ludo@gnu.org> |
| 4 | ;;; Copyright © 2020, 2021, 2023 Janneke Nieuwenhuizen <janneke@gnu.org> | 4 | ;;; Copyright © 2020, 2021, 2023 Janneke Nieuwenhuizen <janneke@gnu.org> |
| 5 | ;;; Copyright © 2021 Timotej Lazar <timotej.lazar@araneo.si> | 5 | ;;; Copyright © 2021 Timotej Lazar <timotej.lazar@araneo.si> |
| 6 | ;;; Copyright © 2022 Oleg Pykhalov <go.wigust@gmail.com> | 6 | ;;; Copyright © 2022 Oleg Pykhalov <go.wigust@gmail.com> |
| @@ -43,6 +43,8 @@ | |||
| 43 | #:use-module (gnu system hurd) | 43 | #:use-module (gnu system hurd) |
| 44 | #:use-module (gnu system image) | 44 | #:use-module (gnu system image) |
| 45 | #:use-module (gnu system shadow) | 45 | #:use-module (gnu system shadow) |
| 46 | #:autoload (gnu system vm) (linux-image-startup-command | ||
| 47 | virtualized-operating-system) | ||
| 46 | #:use-module (gnu system) | 48 | #:use-module (gnu system) |
| 47 | #:use-module (guix derivations) | 49 | #:use-module (guix derivations) |
| 48 | #:use-module (guix gexp) | 50 | #:use-module (guix gexp) |
| @@ -55,12 +57,20 @@ | |||
| 55 | #:autoload (guix self) (make-config.scm) | 57 | #:autoload (guix self) (make-config.scm) |
| 56 | #:autoload (guix platform) (platform-system) | 58 | #:autoload (guix platform) (platform-system) |
| 57 | 59 | ||
| 60 | #:use-module (srfi srfi-1) | ||
| 58 | #:use-module (srfi srfi-9) | 61 | #:use-module (srfi srfi-9) |
| 62 | #:use-module (srfi srfi-19) | ||
| 59 | #:use-module (srfi srfi-26) | 63 | #:use-module (srfi srfi-26) |
| 60 | #:use-module (rnrs bytevectors) | 64 | #:use-module (rnrs bytevectors) |
| 61 | #:use-module (ice-9 match) | 65 | #:use-module (ice-9 match) |
| 62 | 66 | ||
| 63 | #:export (%hurd-vm-operating-system | 67 | #:export (virtual-build-machine |
| 68 | virtual-build-machine-service-type | ||
| 69 | |||
| 70 | %virtual-build-machine-operating-system | ||
| 71 | %virtual-build-machine-default-vm | ||
| 72 | |||
| 73 | %hurd-vm-operating-system | ||
| 64 | hurd-vm-configuration | 74 | hurd-vm-configuration |
| 65 | hurd-vm-configuration? | 75 | hurd-vm-configuration? |
| 66 | hurd-vm-configuration-os | 76 | hurd-vm-configuration-os |
| @@ -1066,6 +1076,461 @@ that will be listening to receive secret keys on ADDRESS." | |||
| 1066 | 1076 | ||
| 1067 | 1077 | ||
| 1068 | ;;; | 1078 | ;;; |
| 1079 | ;;; Offloading-as-a-service. | ||
| 1080 | ;;; | ||
| 1081 | |||
| 1082 | (define-record-type* <virtual-build-machine> | ||
| 1083 | virtual-build-machine make-virtual-build-machine | ||
| 1084 | virtual-build-machine? | ||
| 1085 | this-virtual-build-machine | ||
| 1086 | (name virtual-build-machine-name | ||
| 1087 | (default 'build-vm)) | ||
| 1088 | (image virtual-build-machine-image | ||
| 1089 | (thunked) | ||
| 1090 | (default | ||
| 1091 | (virtual-build-machine-default-image | ||
| 1092 | this-virtual-build-machine))) | ||
| 1093 | (qemu virtual-build-machine-qemu | ||
| 1094 | (default qemu-minimal)) | ||
| 1095 | (cpu virtual-build-machine-cpu | ||
| 1096 | (thunked) | ||
| 1097 | (default | ||
| 1098 | (qemu-cpu-model-for-date | ||
| 1099 | (virtual-build-machine-systems this-virtual-build-machine) | ||
| 1100 | (virtual-build-machine-date this-virtual-build-machine)))) | ||
| 1101 | (cpu-count virtual-build-machine-cpu-count | ||
| 1102 | (default 4)) | ||
| 1103 | (memory-size virtual-build-machine-memory-size ;integer (MiB) | ||
| 1104 | (default 2048)) | ||
| 1105 | (date virtual-build-machine-date | ||
| 1106 | ;; Default to a date "in the past" assuming a common use case | ||
| 1107 | ;; is to rebuild old packages. | ||
| 1108 | (default (make-date 0 0 00 00 01 01 2020 0))) | ||
| 1109 | (port-forwardings virtual-build-machine-port-forwardings | ||
| 1110 | (default | ||
| 1111 | `((,%build-vm-ssh-port . 22) | ||
| 1112 | (,%build-vm-secrets-port . 1004)))) | ||
| 1113 | (systems virtual-build-machine-systems | ||
| 1114 | (default (list (%current-system)))) | ||
| 1115 | (auto-start? virtual-build-machine-auto-start? | ||
| 1116 | (default #f))) | ||
| 1117 | |||
| 1118 | (define %build-vm-ssh-port | ||
| 1119 | ;; Default host port where the guest's SSH port is forwarded. | ||
| 1120 | 11022) | ||
| 1121 | |||
| 1122 | (define %build-vm-secrets-port | ||
| 1123 | ;; Host port to communicate secrets to the build VM. | ||
| 1124 | ;; FIXME: Anyone on the host can talk to it; use virtio ports or AF_VSOCK | ||
| 1125 | ;; instead. | ||
| 1126 | 11044) | ||
| 1127 | |||
| 1128 | (define %x86-64-intel-cpu-models | ||
| 1129 | ;; List of release date/CPU model pairs representing Intel's x86_64 models. | ||
| 1130 | ;; The list is taken from | ||
| 1131 | ;; <https://en.wikipedia.org/wiki/List_of_Intel_CPU_microarchitectures>. | ||
| 1132 | ;; CPU model strings are those found in 'qemu-system-x86_64 -cpu help'. | ||
| 1133 | (letrec-syntax ((cpu-models (syntax-rules () | ||
| 1134 | ((_ (date model) rest ...) | ||
| 1135 | (alist-cons (date->time-utc | ||
| 1136 | (string->date date "~Y-~m-~d")) | ||
| 1137 | model | ||
| 1138 | (cpu-models rest ...))) | ||
| 1139 | ((_) | ||
| 1140 | '())))) | ||
| 1141 | (reverse | ||
| 1142 | (cpu-models ("2006-01-01" "core2duo") | ||
| 1143 | ("2010-01-01" "Westmere") | ||
| 1144 | ("2008-01-01" "Nehalem") | ||
| 1145 | ("2011-01-01" "SandyBridge") | ||
| 1146 | ("2012-01-01" "IvyBridge") | ||
| 1147 | ("2013-01-01" "Haswell") | ||
| 1148 | ("2014-01-01" "Broadwell") | ||
| 1149 | ("2015-01-01" "Skylake-Client"))))) | ||
| 1150 | |||
| 1151 | (define (qemu-cpu-model-for-date systems date) | ||
| 1152 | "Return the QEMU name of a CPU model for SYSTEMS that was current at DATE." | ||
| 1153 | (if (any (cut string-prefix? "x86_64-" <>) systems) | ||
| 1154 | (let ((time (date->time-utc date))) | ||
| 1155 | (any (match-lambda | ||
| 1156 | ((release-date . model) | ||
| 1157 | (and (time<? release-date time) | ||
| 1158 | model))) | ||
| 1159 | %x86-64-intel-cpu-models)) | ||
| 1160 | ;; TODO: Add models for other architectures. | ||
| 1161 | "host")) | ||
| 1162 | |||
| 1163 | (define (virtual-build-machine-ssh-port config) | ||
| 1164 | "Return the host port where CONFIG has its VM's SSH port forwarded." | ||
| 1165 | (any (match-lambda | ||
| 1166 | ((host-port . 22) host-port) | ||
| 1167 | (_ #f)) | ||
| 1168 | (virtual-build-machine-port-forwardings config))) | ||
| 1169 | |||
| 1170 | (define (virtual-build-machine-secrets-port config) | ||
| 1171 | "Return the host port where CONFIG has its VM's secrets port forwarded." | ||
| 1172 | (any (match-lambda | ||
| 1173 | ((host-port . 1004) host-port) | ||
| 1174 | (_ #f)) | ||
| 1175 | (virtual-build-machine-port-forwardings config))) | ||
| 1176 | |||
| 1177 | (define %minimal-vm-syslog-config | ||
| 1178 | ;; Minimal syslog configuration for a VM. | ||
| 1179 | (plain-file "vm-syslog.conf" "\ | ||
| 1180 | # Log most messages to the console, which goes to the serial | ||
| 1181 | # output, allowing the host to log it. | ||
| 1182 | *.info;auth.notice;authpriv.none -/dev/console | ||
| 1183 | |||
| 1184 | # The rest. | ||
| 1185 | *.=debug -/var/log/debug | ||
| 1186 | authpriv.*;auth.info /var/log/secure | ||
| 1187 | ")) | ||
| 1188 | |||
| 1189 | (define %virtual-build-machine-operating-system | ||
| 1190 | (operating-system | ||
| 1191 | (host-name "build-machine") | ||
| 1192 | (bootloader (bootloader-configuration ;unused | ||
| 1193 | (bootloader grub-minimal-bootloader) | ||
| 1194 | (targets '("/dev/null")))) | ||
| 1195 | (file-systems (list (file-system ;unused | ||
| 1196 | (mount-point "/") | ||
| 1197 | (device "none") | ||
| 1198 | (type "tmpfs")))) | ||
| 1199 | (users (cons (user-account | ||
| 1200 | (name "offload") | ||
| 1201 | (group "users") | ||
| 1202 | (supplementary-groups '("kvm")) | ||
| 1203 | (comment "Account used for offloading")) | ||
| 1204 | %base-user-accounts)) | ||
| 1205 | (services (cons* (service static-networking-service-type | ||
| 1206 | (list %qemu-static-networking)) | ||
| 1207 | (service openssh-service-type | ||
| 1208 | (openssh-configuration | ||
| 1209 | (openssh openssh-sans-x))) | ||
| 1210 | |||
| 1211 | (modify-services %base-services | ||
| 1212 | ;; By default, the secret service introduces a | ||
| 1213 | ;; pre-initialized /etc/guix/acl file in the VM. Thus, | ||
| 1214 | ;; clear 'authorize-key?' so that it's not overridden | ||
| 1215 | ;; at activation time. | ||
| 1216 | (guix-service-type config => | ||
| 1217 | (guix-configuration | ||
| 1218 | (inherit config) | ||
| 1219 | (authorize-key? #f))) | ||
| 1220 | (syslog-service-type config => | ||
| 1221 | (syslog-configuration | ||
| 1222 | (config-file | ||
| 1223 | %minimal-vm-syslog-config))) | ||
| 1224 | (delete mingetty-service-type) | ||
| 1225 | (delete console-font-service-type)))))) | ||
| 1226 | |||
| 1227 | (define (virtual-build-machine-default-image config) | ||
| 1228 | (let* ((type (lookup-image-type-by-name 'mbr-raw)) | ||
| 1229 | (base (os->image %virtual-build-machine-operating-system | ||
| 1230 | #:type type))) | ||
| 1231 | (image (inherit base) | ||
| 1232 | (name (symbol-append 'build-vm- | ||
| 1233 | (virtual-build-machine-name config))) | ||
| 1234 | (format 'compressed-qcow2) | ||
| 1235 | (partition-table-type 'mbr) | ||
| 1236 | (shared-store? #f) | ||
| 1237 | (size (* 10 (expt 2 30)))))) | ||
| 1238 | |||
| 1239 | (define (virtual-build-machine-account-name config) | ||
| 1240 | (string-append "build-vm-" | ||
| 1241 | (symbol->string | ||
| 1242 | (virtual-build-machine-name config)))) | ||
| 1243 | |||
| 1244 | (define (virtual-build-machine-accounts config) | ||
| 1245 | (let ((name (virtual-build-machine-account-name config))) | ||
| 1246 | (list (user-group (name name) (system? #t)) | ||
| 1247 | (user-account | ||
| 1248 | (name name) | ||
| 1249 | (group name) | ||
| 1250 | (supplementary-groups '("kvm")) | ||
| 1251 | (comment "Privilege separation user for the virtual build machine") | ||
| 1252 | (home-directory "/var/empty") | ||
| 1253 | (shell (file-append shadow "/sbin/nologin")) | ||
| 1254 | (system? #t))))) | ||
| 1255 | |||
| 1256 | (define (build-vm-shepherd-services config) | ||
| 1257 | (define transform | ||
| 1258 | (compose secret-service-operating-system | ||
| 1259 | operating-system-with-locked-root-account | ||
| 1260 | operating-system-with-offloading-account | ||
| 1261 | (lambda (os) | ||
| 1262 | (virtualized-operating-system os #:full-boot? #t)))) | ||
| 1263 | |||
| 1264 | (define transformed-image | ||
| 1265 | (let ((base (virtual-build-machine-image config))) | ||
| 1266 | (image | ||
| 1267 | (inherit base) | ||
| 1268 | (operating-system | ||
| 1269 | (transform (image-operating-system base)))))) | ||
| 1270 | |||
| 1271 | (define command | ||
| 1272 | (linux-image-startup-command transformed-image | ||
| 1273 | #:qemu | ||
| 1274 | (virtual-build-machine-qemu config) | ||
| 1275 | #:cpu | ||
| 1276 | (virtual-build-machine-cpu config) | ||
| 1277 | #:cpu-count | ||
| 1278 | (virtual-build-machine-cpu-count config) | ||
| 1279 | #:memory-size | ||
| 1280 | (virtual-build-machine-memory-size config) | ||
| 1281 | #:port-forwardings | ||
| 1282 | (virtual-build-machine-port-forwardings | ||
| 1283 | config) | ||
| 1284 | #:date | ||
| 1285 | (virtual-build-machine-date config))) | ||
| 1286 | |||
| 1287 | (define user | ||
| 1288 | (virtual-build-machine-account-name config)) | ||
| 1289 | |||
| 1290 | (list (shepherd-service | ||
| 1291 | (documentation "Run the build virtual machine service.") | ||
| 1292 | (provision (list (virtual-build-machine-name config))) | ||
| 1293 | (requirement '(user-processes)) | ||
| 1294 | (modules `((gnu build secret-service) | ||
| 1295 | (guix build utils) | ||
| 1296 | ,@%default-modules)) | ||
| 1297 | (start | ||
| 1298 | (with-imported-modules (source-module-closure | ||
| 1299 | '((gnu build secret-service) | ||
| 1300 | (guix build utils))) | ||
| 1301 | #~(lambda arguments | ||
| 1302 | (let* ((pid (fork+exec-command (append #$command arguments) | ||
| 1303 | #:user #$user | ||
| 1304 | #:group "kvm" | ||
| 1305 | #:environment-variables | ||
| 1306 | ;; QEMU tries to write to /var/tmp | ||
| 1307 | ;; by default. | ||
| 1308 | '("TMPDIR=/tmp"))) | ||
| 1309 | (port #$(virtual-build-machine-secrets-port config)) | ||
| 1310 | (root #$(virtual-build-machine-secret-root config)) | ||
| 1311 | (address (make-socket-address AF_INET INADDR_LOOPBACK | ||
| 1312 | port))) | ||
| 1313 | (catch #t | ||
| 1314 | (lambda _ | ||
| 1315 | (if (secret-service-send-secrets address root) | ||
| 1316 | pid | ||
| 1317 | (begin | ||
| 1318 | (kill (- pid) SIGTERM) | ||
| 1319 | #f))) | ||
| 1320 | (lambda (key . args) | ||
| 1321 | (kill (- pid) SIGTERM) | ||
| 1322 | (apply throw key args))))))) | ||
| 1323 | (stop #~(make-kill-destructor)) | ||
| 1324 | (auto-start? (virtual-build-machine-auto-start? config))))) | ||
| 1325 | |||
| 1326 | (define (authorize-guest-substitutes-on-host) | ||
| 1327 | "Return a program that authorizes the guest's archive signing key (passed as | ||
| 1328 | an argument) on the host." | ||
| 1329 | (define not-config? | ||
| 1330 | (match-lambda | ||
| 1331 | ('(guix config) #f) | ||
| 1332 | (('guix _ ...) #t) | ||
| 1333 | (('gnu _ ...) #t) | ||
| 1334 | (_ #f))) | ||
| 1335 | |||
| 1336 | (define run | ||
| 1337 | (with-extensions (list guile-gcrypt) | ||
| 1338 | (with-imported-modules `(((guix config) => ,(make-config.scm)) | ||
| 1339 | ,@(source-module-closure | ||
| 1340 | '((guix pki) | ||
| 1341 | (guix build utils)) | ||
| 1342 | #:select? not-config?)) | ||
| 1343 | #~(begin | ||
| 1344 | (use-modules (ice-9 match) | ||
| 1345 | (ice-9 textual-ports) | ||
| 1346 | (gcrypt pk-crypto) | ||
| 1347 | (guix pki) | ||
| 1348 | (guix build utils)) | ||
| 1349 | |||
| 1350 | (match (command-line) | ||
| 1351 | ((_ guest-config-directory) | ||
| 1352 | (let ((guest-key (string-append guest-config-directory | ||
| 1353 | "/signing-key.pub"))) | ||
| 1354 | (if (file-exists? guest-key) | ||
| 1355 | ;; Add guest key to the host's ACL. | ||
| 1356 | (let* ((key (string->canonical-sexp | ||
| 1357 | (call-with-input-file guest-key | ||
| 1358 | get-string-all))) | ||
| 1359 | (acl (public-keys->acl | ||
| 1360 | (cons key (acl->public-keys (current-acl)))))) | ||
| 1361 | (with-atomic-file-replacement %acl-file | ||
| 1362 | (lambda (_ port) | ||
| 1363 | (write-acl acl port)))) | ||
| 1364 | (format (current-error-port) | ||
| 1365 | "warning: guest key missing from '~a'~%" | ||
| 1366 | guest-key))))))))) | ||
| 1367 | |||
| 1368 | (program-file "authorize-guest-substitutes-on-host" run)) | ||
| 1369 | |||
| 1370 | (define (initialize-build-vm-substitutes) | ||
| 1371 | "Initialize the Hurd VM's key pair and ACL and store it on the host." | ||
| 1372 | (define run | ||
| 1373 | (with-imported-modules '((guix build utils)) | ||
| 1374 | #~(begin | ||
| 1375 | (use-modules (guix build utils) | ||
| 1376 | (ice-9 match)) | ||
| 1377 | |||
| 1378 | (define host-key | ||
| 1379 | "/etc/guix/signing-key.pub") | ||
| 1380 | |||
| 1381 | (define host-acl | ||
| 1382 | "/etc/guix/acl") | ||
| 1383 | |||
| 1384 | (match (command-line) | ||
| 1385 | ((_ guest-config-directory) | ||
| 1386 | (setenv "GUIX_CONFIGURATION_DIRECTORY" | ||
| 1387 | guest-config-directory) | ||
| 1388 | (invoke #+(file-append guix "/bin/guix") "archive" | ||
| 1389 | "--generate-key") | ||
| 1390 | |||
| 1391 | (when (file-exists? host-acl) | ||
| 1392 | ;; Copy the host ACL. | ||
| 1393 | (copy-file host-acl | ||
| 1394 | (string-append guest-config-directory | ||
| 1395 | "/acl"))) | ||
| 1396 | |||
| 1397 | (when (file-exists? host-key) | ||
| 1398 | ;; Add the host key to the childhurd's ACL. | ||
| 1399 | (let ((key (open-fdes host-key O_RDONLY))) | ||
| 1400 | (close-fdes 0) | ||
| 1401 | (dup2 key 0) | ||
| 1402 | (execl #+(file-append guix "/bin/guix") | ||
| 1403 | "guix" "archive" "--authorize")))))))) | ||
| 1404 | |||
| 1405 | (program-file "initialize-build-vm-substitutes" run)) | ||
| 1406 | |||
| 1407 | (define* (build-vm-activation secret-directory | ||
| 1408 | #:key | ||
| 1409 | offloading-ssh-key | ||
| 1410 | (offloading? #t)) | ||
| 1411 | (with-imported-modules '((guix build utils)) | ||
| 1412 | #~(begin | ||
| 1413 | (use-modules (guix build utils)) | ||
| 1414 | |||
| 1415 | (define secret-directory | ||
| 1416 | #$secret-directory) | ||
| 1417 | |||
| 1418 | (define ssh-directory | ||
| 1419 | (string-append secret-directory "/etc/ssh")) | ||
| 1420 | |||
| 1421 | (define guix-directory | ||
| 1422 | (string-append secret-directory "/etc/guix")) | ||
| 1423 | |||
| 1424 | (define offloading-ssh-key | ||
| 1425 | #$offloading-ssh-key) | ||
| 1426 | |||
| 1427 | (unless (file-exists? ssh-directory) | ||
| 1428 | ;; Generate SSH host keys under SSH-DIRECTORY. | ||
| 1429 | (mkdir-p ssh-directory) | ||
| 1430 | (invoke #$(file-append openssh "/bin/ssh-keygen") | ||
| 1431 | "-A" "-f" secret-directory)) | ||
| 1432 | |||
| 1433 | (unless (or (not #$offloading?) | ||
| 1434 | (file-exists? offloading-ssh-key)) | ||
| 1435 | ;; Generate a user SSH key pair for the host to use when offloading | ||
| 1436 | ;; to the guest. | ||
| 1437 | (mkdir-p (dirname offloading-ssh-key)) | ||
| 1438 | (invoke #$(file-append openssh "/bin/ssh-keygen") | ||
| 1439 | "-t" "ed25519" "-N" "" | ||
| 1440 | "-f" offloading-ssh-key) | ||
| 1441 | |||
| 1442 | ;; Authorize it in the guest for user 'offloading'. | ||
| 1443 | (let ((authorizations | ||
| 1444 | (string-append ssh-directory | ||
| 1445 | "/authorized_keys.d/offloading"))) | ||
| 1446 | (mkdir-p (dirname authorizations)) | ||
| 1447 | (copy-file (string-append offloading-ssh-key ".pub") | ||
| 1448 | authorizations) | ||
| 1449 | (chmod (dirname authorizations) #o555))) | ||
| 1450 | |||
| 1451 | (unless (file-exists? guix-directory) | ||
| 1452 | (invoke #$(initialize-build-vm-substitutes) | ||
| 1453 | guix-directory)) | ||
| 1454 | |||
| 1455 | (when #$offloading? | ||
| 1456 | ;; Authorize the archive signing key from GUIX-DIRECTORY in the host. | ||
| 1457 | (invoke #$(authorize-guest-substitutes-on-host) guix-directory))))) | ||
| 1458 | |||
| 1459 | (define (virtual-build-machine-offloading-ssh-key config) | ||
| 1460 | "Return the name of the file containing the SSH key of user 'offloading'." | ||
| 1461 | (string-append "/etc/guix/offload/ssh/virtual-build-machine/" | ||
| 1462 | (symbol->string | ||
| 1463 | (virtual-build-machine-name config)))) | ||
| 1464 | |||
| 1465 | (define (virtual-build-machine-activation config) | ||
| 1466 | "Return a gexp to activate the build VM according to CONFIG." | ||
| 1467 | (build-vm-activation (virtual-build-machine-secret-root config) | ||
| 1468 | #:offloading? #t | ||
| 1469 | #:offloading-ssh-key | ||
| 1470 | (virtual-build-machine-offloading-ssh-key config))) | ||
| 1471 | |||
| 1472 | (define (virtual-build-machine-secret-root config) | ||
| 1473 | (string-append "/etc/guix/virtual-build-machines/" | ||
| 1474 | (symbol->string | ||
| 1475 | (virtual-build-machine-name config)))) | ||
| 1476 | |||
| 1477 | (define (check-vm-availability config) | ||
| 1478 | "Return a Scheme file that evaluates to true if the service corresponding to | ||
| 1479 | CONFIG, a <virtual-build-machine>, is up and running." | ||
| 1480 | (define service-name | ||
| 1481 | (virtual-build-machine-name config)) | ||
| 1482 | |||
| 1483 | (scheme-file "check-build-vm-availability.scm" | ||
| 1484 | #~(begin | ||
| 1485 | (use-modules (gnu services herd) | ||
| 1486 | (srfi srfi-34)) | ||
| 1487 | |||
| 1488 | (guard (c ((service-not-found-error? c) #f)) | ||
| 1489 | (->bool (current-service '#$service-name)))))) | ||
| 1490 | |||
| 1491 | (define (build-vm-guix-extension config) | ||
| 1492 | (define vm-ssh-key | ||
| 1493 | (string-append | ||
| 1494 | (virtual-build-machine-secret-root config) | ||
| 1495 | "/etc/ssh/ssh_host_ed25519_key.pub")) | ||
| 1496 | |||
| 1497 | (define host-ssh-key | ||
| 1498 | (virtual-build-machine-offloading-ssh-key config)) | ||
| 1499 | |||
| 1500 | (guix-extension | ||
| 1501 | (build-machines | ||
| 1502 | (list #~(if (primitive-load #$(check-vm-availability config)) | ||
| 1503 | (list (build-machine | ||
| 1504 | (name "localhost") | ||
| 1505 | (port #$(virtual-build-machine-ssh-port config)) | ||
| 1506 | (systems | ||
| 1507 | '#$(virtual-build-machine-systems config)) | ||
| 1508 | (user "offloading") | ||
| 1509 | (host-key (call-with-input-file #$vm-ssh-key | ||
| 1510 | (@ (ice-9 textual-ports) | ||
| 1511 | get-string-all))) | ||
| 1512 | (private-key #$host-ssh-key))) | ||
| 1513 | '()))))) | ||
| 1514 | |||
| 1515 | (define virtual-build-machine-service-type | ||
| 1516 | (service-type | ||
| 1517 | (name 'build-vm) | ||
| 1518 | (extensions (list (service-extension shepherd-root-service-type | ||
| 1519 | build-vm-shepherd-services) | ||
| 1520 | (service-extension guix-service-type | ||
| 1521 | build-vm-guix-extension) | ||
| 1522 | (service-extension account-service-type | ||
| 1523 | virtual-build-machine-accounts) | ||
| 1524 | (service-extension activation-service-type | ||
| 1525 | virtual-build-machine-activation))) | ||
| 1526 | (description | ||
| 1527 | "Create a @dfn{virtual build machine}: a virtual machine (VM) that builds | ||
| 1528 | can be offloaded to. By default, the virtual machine starts with a clock | ||
| 1529 | running at some point in the past.") | ||
| 1530 | (default-value (virtual-build-machine)))) | ||
| 1531 | |||
| 1532 | |||
| 1533 | ;;; | ||
| 1069 | ;;; The Hurd in VM service: a Childhurd. | 1534 | ;;; The Hurd in VM service: a Childhurd. |
| 1070 | ;;; | 1535 | ;;; |
| 1071 | 1536 | ||
| @@ -1290,136 +1755,13 @@ is added to the OS specified in CONFIG." | |||
| 1290 | (shell (file-append shadow "/sbin/nologin")) | 1755 | (shell (file-append shadow "/sbin/nologin")) |
| 1291 | (system? #t)))) | 1756 | (system? #t)))) |
| 1292 | 1757 | ||
| 1293 | (define (initialize-hurd-vm-substitutes) | ||
| 1294 | "Initialize the Hurd VM's key pair and ACL and store it on the host." | ||
| 1295 | (define run | ||
| 1296 | (with-imported-modules '((guix build utils)) | ||
| 1297 | #~(begin | ||
| 1298 | (use-modules (guix build utils) | ||
| 1299 | (ice-9 match)) | ||
| 1300 | |||
| 1301 | (define host-key | ||
| 1302 | "/etc/guix/signing-key.pub") | ||
| 1303 | |||
| 1304 | (define host-acl | ||
| 1305 | "/etc/guix/acl") | ||
| 1306 | |||
| 1307 | (match (command-line) | ||
| 1308 | ((_ guest-config-directory) | ||
| 1309 | (setenv "GUIX_CONFIGURATION_DIRECTORY" | ||
| 1310 | guest-config-directory) | ||
| 1311 | (invoke #+(file-append guix "/bin/guix") "archive" | ||
| 1312 | "--generate-key") | ||
| 1313 | |||
| 1314 | (when (file-exists? host-acl) | ||
| 1315 | ;; Copy the host ACL. | ||
| 1316 | (copy-file host-acl | ||
| 1317 | (string-append guest-config-directory | ||
| 1318 | "/acl"))) | ||
| 1319 | |||
| 1320 | (when (file-exists? host-key) | ||
| 1321 | ;; Add the host key to the childhurd's ACL. | ||
| 1322 | (let ((key (open-fdes host-key O_RDONLY))) | ||
| 1323 | (close-fdes 0) | ||
| 1324 | (dup2 key 0) | ||
| 1325 | (execl #+(file-append guix "/bin/guix") | ||
| 1326 | "guix" "archive" "--authorize")))))))) | ||
| 1327 | |||
| 1328 | (program-file "initialize-hurd-vm-substitutes" run)) | ||
| 1329 | |||
| 1330 | (define (authorize-guest-substitutes-on-host) | ||
| 1331 | "Return a program that authorizes the guest's archive signing key (passed as | ||
| 1332 | an argument) on the host." | ||
| 1333 | (define not-config? | ||
| 1334 | (match-lambda | ||
| 1335 | ('(guix config) #f) | ||
| 1336 | (('guix _ ...) #t) | ||
| 1337 | (('gnu _ ...) #t) | ||
| 1338 | (_ #f))) | ||
| 1339 | |||
| 1340 | (define run | ||
| 1341 | (with-extensions (list guile-gcrypt) | ||
| 1342 | (with-imported-modules `(((guix config) => ,(make-config.scm)) | ||
| 1343 | ,@(source-module-closure | ||
| 1344 | '((guix pki) | ||
| 1345 | (guix build utils)) | ||
| 1346 | #:select? not-config?)) | ||
| 1347 | #~(begin | ||
| 1348 | (use-modules (ice-9 match) | ||
| 1349 | (ice-9 textual-ports) | ||
| 1350 | (gcrypt pk-crypto) | ||
| 1351 | (guix pki) | ||
| 1352 | (guix build utils)) | ||
| 1353 | |||
| 1354 | (match (command-line) | ||
| 1355 | ((_ guest-config-directory) | ||
| 1356 | (let ((guest-key (string-append guest-config-directory | ||
| 1357 | "/signing-key.pub"))) | ||
| 1358 | (if (file-exists? guest-key) | ||
| 1359 | ;; Add guest key to the host's ACL. | ||
| 1360 | (let* ((key (string->canonical-sexp | ||
| 1361 | (call-with-input-file guest-key | ||
| 1362 | get-string-all))) | ||
| 1363 | (acl (public-keys->acl | ||
| 1364 | (cons key (acl->public-keys (current-acl)))))) | ||
| 1365 | (with-atomic-file-replacement %acl-file | ||
| 1366 | (lambda (_ port) | ||
| 1367 | (write-acl acl port)))) | ||
| 1368 | (format (current-error-port) | ||
| 1369 | "warning: guest key missing from '~a'~%" | ||
| 1370 | guest-key))))))))) | ||
| 1371 | |||
| 1372 | (program-file "authorize-guest-substitutes-on-host" run)) | ||
| 1373 | |||
| 1374 | (define (hurd-vm-activation config) | 1758 | (define (hurd-vm-activation config) |
| 1375 | "Return a gexp to activate the Hurd VM according to CONFIG." | 1759 | "Return a gexp to activate the Hurd VM according to CONFIG." |
| 1376 | (with-imported-modules '((guix build utils)) | 1760 | (build-vm-activation (hurd-vm-configuration-secret-root config) |
| 1377 | #~(begin | 1761 | #:offloading? |
| 1378 | (use-modules (guix build utils)) | 1762 | (hurd-vm-configuration-offloading? config) |
| 1379 | 1763 | #:offloading-ssh-key | |
| 1380 | (define secret-directory | 1764 | (hurd-vm-configuration-offloading-ssh-key config))) |
| 1381 | #$(hurd-vm-configuration-secret-root config)) | ||
| 1382 | |||
| 1383 | (define ssh-directory | ||
| 1384 | (string-append secret-directory "/etc/ssh")) | ||
| 1385 | |||
| 1386 | (define guix-directory | ||
| 1387 | (string-append secret-directory "/etc/guix")) | ||
| 1388 | |||
| 1389 | (define offloading-ssh-key | ||
| 1390 | #$(hurd-vm-configuration-offloading-ssh-key config)) | ||
| 1391 | |||
| 1392 | (unless (file-exists? ssh-directory) | ||
| 1393 | ;; Generate SSH host keys under SSH-DIRECTORY. | ||
| 1394 | (mkdir-p ssh-directory) | ||
| 1395 | (invoke #$(file-append openssh "/bin/ssh-keygen") | ||
| 1396 | "-A" "-f" secret-directory)) | ||
| 1397 | |||
| 1398 | (unless (or (not #$(hurd-vm-configuration-offloading? config)) | ||
| 1399 | (file-exists? offloading-ssh-key)) | ||
| 1400 | ;; Generate a user SSH key pair for the host to use when offloading | ||
| 1401 | ;; to the guest. | ||
| 1402 | (mkdir-p (dirname offloading-ssh-key)) | ||
| 1403 | (invoke #$(file-append openssh "/bin/ssh-keygen") | ||
| 1404 | "-t" "ed25519" "-N" "" | ||
| 1405 | "-f" offloading-ssh-key) | ||
| 1406 | |||
| 1407 | ;; Authorize it in the guest for user 'offloading'. | ||
| 1408 | (let ((authorizations | ||
| 1409 | (string-append ssh-directory | ||
| 1410 | "/authorized_keys.d/offloading"))) | ||
| 1411 | (mkdir-p (dirname authorizations)) | ||
| 1412 | (copy-file (string-append offloading-ssh-key ".pub") | ||
| 1413 | authorizations) | ||
| 1414 | (chmod (dirname authorizations) #o555))) | ||
| 1415 | |||
| 1416 | (unless (file-exists? guix-directory) | ||
| 1417 | (invoke #$(initialize-hurd-vm-substitutes) | ||
| 1418 | guix-directory)) | ||
| 1419 | |||
| 1420 | (when #$(hurd-vm-configuration-offloading? config) | ||
| 1421 | ;; Authorize the archive signing key from GUIX-DIRECTORY in the host. | ||
| 1422 | (invoke #$(authorize-guest-substitutes-on-host) guix-directory))))) | ||
| 1423 | 1765 | ||
| 1424 | (define (hurd-vm-configuration-offloading-ssh-key config) | 1766 | (define (hurd-vm-configuration-offloading-ssh-key config) |
| 1425 | "Return the name of the file containing the SSH key of user 'offloading'." | 1767 | "Return the name of the file containing the SSH key of user 'offloading'." |
