From patchwork Mon Aug 31 06:39:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Janneke Nieuwenhuizen X-Patchwork-Id: 23838 Return-Path: X-Original-To: patchwork@mira.cbaines.net Delivered-To: patchwork@mira.cbaines.net Received: by mira.cbaines.net (Postfix, from userid 113) id 40B4F27BBE6; Mon, 31 Aug 2020 07:40:14 +0100 (BST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mira.cbaines.net X-Spam-Level: X-Spam-Status: No, score=-2.9 required=5.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mira.cbaines.net (Postfix) with ESMTP id 9A06227BBE5 for ; Mon, 31 Aug 2020 07:40:12 +0100 (BST) Received: from localhost ([::1]:41166 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kCdUC-0001Ji-2B for patchwork@mira.cbaines.net; Mon, 31 Aug 2020 02:40:12 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:45914) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kCdU3-0001Ir-82 for guix-patches@gnu.org; Mon, 31 Aug 2020 02:40:03 -0400 Received: from debbugs.gnu.org ([209.51.188.43]:39822) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kCdU2-00012A-Uz for guix-patches@gnu.org; Mon, 31 Aug 2020 02:40:02 -0400 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1kCdU2-0000Q7-Sm for guix-patches@gnu.org; Mon, 31 Aug 2020 02:40:02 -0400 X-Loop: help-debbugs@gnu.org Subject: [bug#43106] [PATCH v3 1/2] services: Add secret-service-type. Resent-From: "Jan (janneke) Nieuwenhuizen" Original-Sender: "Debbugs-submit" Resent-CC: guix-patches@gnu.org Resent-Date: Mon, 31 Aug 2020 06:40:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 43106 X-GNU-PR-Package: guix-patches X-GNU-PR-Keywords: patch To: Ludovic =?utf-8?q?Court=C3=A8s?= , 43106@debbugs.gnu.org Received: via spool by 43106-submit@debbugs.gnu.org id=B43106.15988559821565 (code B ref 43106); Mon, 31 Aug 2020 06:40:02 +0000 Received: (at 43106) by debbugs.gnu.org; 31 Aug 2020 06:39:42 +0000 Received: from localhost ([127.0.0.1]:51365 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kCdTg-0000P5-3W for submit@debbugs.gnu.org; Mon, 31 Aug 2020 02:39:42 -0400 Received: from eggs.gnu.org ([209.51.188.92]:35764) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1kCdTT-0000OL-Lf for 43106@debbugs.gnu.org; Mon, 31 Aug 2020 02:39:31 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]:51265) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kCdTO-00011R-20; Mon, 31 Aug 2020 02:39:22 -0400 Received: from [2001:980:1b4f:1:42d2:832d:bb59:862] (port=35160 helo=dundal.fritz.box) by fencepost.gnu.org with esmtpa (Exim 4.82) (envelope-from ) id 1kCdTN-0002CR-Fi; Mon, 31 Aug 2020 02:39:21 -0400 From: "Jan (janneke) Nieuwenhuizen" Date: Mon, 31 Aug 2020 08:39:12 +0200 Message-Id: <20200831063913.664-2-janneke@gnu.org> X-Mailer: git-send-email 2.28.0 In-Reply-To: <20200831063913.664-1-janneke@gnu.org> References: <20200831063913.664-1-janneke@gnu.org> MIME-Version: 1.0 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: guix-patches@gnu.org List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+patchwork=mira.cbaines.net@gnu.org Sender: "Guix-patches" X-getmail-retrieved-from-mailbox: Patches This adds a "secret-service" that can be added to a Childhurd VM to receive out-of-band secrets (keys) sent from the host. Co-authored-by: Ludovic Courtès * gnu/services/virtualization.scm (secret-service-activation): New procedure. (secret-service-type): New variable. * gnu/build/secret-service.scm: New file. * gnu/local.mk (GNU_SYSTEM_MODULES): Add it. --- gnu/build/secret-service.scm | 138 ++++++++++++++++++++++++++++++++ gnu/local.mk | 1 + gnu/services/virtualization.scm | 29 ++++++- 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 gnu/build/secret-service.scm diff --git a/gnu/build/secret-service.scm b/gnu/build/secret-service.scm new file mode 100644 index 0000000000..aa88f8c209 --- /dev/null +++ b/gnu/build/secret-service.scm @@ -0,0 +1,138 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2020 Ludovic Courtès +;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen +;;; +;;; 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 . + +(define-module (gnu build secret-service) + #:use-module (guix build utils) + + #:use-module (srfi srfi-26) + #:use-module (rnrs bytevectors) + #:use-module (ice-9 binary-ports) + #:use-module (ice-9 match) + #:use-module (ice-9 rdelim) + + #:export (secret-service-receive-secrets + secret-service-send-secrets)) + +;;; Commentary: +;;; +;;; Utility procedures for copying secrets into a VM. +;;; +;;; Code: + +(define* (secret-service-send-secrets port secret-root #:key (retry 60)) + "Copy all files under SECRET-ROOT using TCP to secret-service listening at +local PORT." + + (define (file->file+size+mode file-name) + (let ((stat (stat file-name)) + (target (substring file-name (string-length secret-root)))) + (list target (stat:size stat) (stat:mode stat)))) + + (format (current-error-port) "secret-service-send-secrets\n") + + (let ((sock (socket AF_INET SOCK_STREAM 0)) + (addr (make-socket-address AF_INET INADDR_LOOPBACK port))) + ;; connect to wait for port + (let loop ((retry retry)) + (if (zero? retry) + (error "connecting to childhurd failed") + (catch 'system-error + (lambda _ + (connect sock addr)) + (lambda (key . args) + (format (current-error-port) "connect failed: ~a ~s\n" key args) + (sleep 1) + (loop (1- retry)))))) + (format (current-error-port) "connected!\n") + ;; copy tree + (let* ((files (if secret-root (find-files secret-root) '())) + (files-sizes-modes (map file->file+size+mode files)) + (secrets `(secrets + (version 0) + (files ,files-sizes-modes)))) + (write secrets sock) + (for-each (compose (cute display <> sock) + (cute with-input-from-file <> read-string)) + files)))) + +(define (secret-service-receive-secrets port) + "Listen to local PORT and wait for a secret service client to send secrets. +Write them to the file system." + + (define (wait-for-client port) + ;; Wait for a TCP connection on PORT. Note: We cannot use the + ;; virtio-serial ports, which would be safer, because they are + ;; (presumably) unsupported on GNU/Hurd. + (let ((sock (socket AF_INET SOCK_STREAM 0))) + (bind sock AF_INET INADDR_ANY port) + (listen sock 1) + (format (current-error-port) + "waiting for secrets on port ~a...~%" + port) + (match (accept sock) + ((client . address) + (format (current-error-port) "client connection from ~a~%" + (inet-ntop (sockaddr:fam address) + (sockaddr:addr address))) + (close-port sock) + client)))) + + ;; TODO: Remove when (@ (guix build utils) dump-port) has a 'size' + ;; parameter. + (define (dump in out size) + ;; Copy SIZE bytes from IN to OUT. + (define buf-size 65536) + (define buf (make-bytevector buf-size)) + + (let loop ((left size)) + (if (<= left 0) + 0 + (let ((read (get-bytevector-n! in buf 0 (min left buf-size)))) + (if (eof-object? read) + left + (begin + (put-bytevector out buf 0 read) + (loop (- left read)))))))) + + (define (read-secrets port) + ;; Read secret files from PORT and install them. + (match (false-if-exception (read port)) + (('secrets ('version 0) + ('files ((files sizes modes) ...))) + (for-each (lambda (file size mode) + (format (current-error-port) + "installing file '~a' (~a bytes)...~%" + file size) + (mkdir-p (dirname file)) + (call-with-output-file file + (lambda (output) + (dump port output size) + (chmod file mode)))) + files sizes modes)) + (_ + (format (current-error-port) + "invalid secrets received~%") + #f))) + + (let* ((port (wait-for-client port)) + (result (read-secrets port))) + (close-port port) + result)) + +;;; secret-service.scm ends here diff --git a/gnu/local.mk b/gnu/local.mk index 8854698178..1d8022fd11 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -659,6 +659,7 @@ GNU_SYSTEM_MODULES = \ %D%/build/linux-initrd.scm \ %D%/build/linux-modules.scm \ %D%/build/marionette.scm \ + %D%/build/secret-service.scm \ %D%/build/vm.scm \ \ %D%/tests.scm \ diff --git a/gnu/services/virtualization.scm b/gnu/services/virtualization.scm index b93ed70099..6d6734dcd1 100644 --- a/gnu/services/virtualization.scm +++ b/gnu/services/virtualization.scm @@ -1,6 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017 Ryan Moe -;;; Copyright © 2018 Ludovic Courtès +;;; Copyright © 2018, 2020 Ludovic Courtès ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen ;;; ;;; This file is part of GNU Guix. @@ -804,6 +804,33 @@ given QEMU package." compiled for other architectures using QEMU and the @code{binfmt_misc} functionality of the kernel Linux."))) + +;;; +;;; Secrets for guest VMs. +;;; + +(define (secret-service-activation port) + "Return an activation snippet that fetches sensitive material at local PORT, +over TCP. Reboot upon failure." + (with-imported-modules '((gnu build secret-service) + (guix build utils)) + #~(begin + (use-modules (gnu build secret-service)) + (let ((sent (secret-service-receive-secrets #$port))) + (unless sent + (sleep 3) + (reboot)))))) + +(define secret-service-type + (service-type + (name 'secret-service) + (extensions (list (service-extension activation-service-type + secret-service-activation))) + (description + "This service fetches secret key and other sensitive material over TCP at +boot time. This service is meant to be used by virtual machines (VMs) that +can only be accessed by their host."))) + ;;; ;;; The Hurd in VM service: a Childhurd.