summaryrefslogtreecommitdiff
path: root/gnu/build/secret-service.scm
diff options
context:
space:
mode:
authorGuillaume Le Vaillant <glv@posteo.net>2020-10-05 14:17:25 +0200
committerGuillaume Le Vaillant <glv@posteo.net>2020-10-05 14:17:25 +0200
commit87c079d9b55afda249ddc1b11798a62547a2cbb6 (patch)
treea7a0dbcfd8c3fb8935e00cc44f8b514fa790975b /gnu/build/secret-service.scm
parentde96ed11efdfb450ca45952aceda656a78d981c4 (diff)
parent3699ed63501a28629956ca60e198f5fafa57ad4e (diff)
downloadguix-patches-87c079d9b55afda249ddc1b11798a62547a2cbb6.tar
guix-patches-87c079d9b55afda249ddc1b11798a62547a2cbb6.tar.gz
Merge branch 'master' into staging
Diffstat (limited to 'gnu/build/secret-service.scm')
-rw-r--r--gnu/build/secret-service.scm121
1 files changed, 87 insertions, 34 deletions
diff --git a/gnu/build/secret-service.scm b/gnu/build/secret-service.scm
index 781651e90d..46dcf1b9c3 100644
--- a/gnu/build/secret-service.scm
+++ b/gnu/build/secret-service.scm
@@ -35,44 +35,86 @@
;;;
;;; 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. If connect fails, sleep 1s and retry RETRY times."
+(define-syntax log
+ (lambda (s)
+ "Log the given message."
+ (syntax-case s ()
+ ((_ fmt args ...)
+ (with-syntax ((fmt (string-append "secret service: "
+ (syntax->datum #'fmt))))
+ ;; Log to the current output port. That way, when
+ ;; 'secret-service-send-secrets' is called from shepherd, output goes
+ ;; to syslog.
+ #'(format (current-output-port) fmt args ...))))))
+(define* (secret-service-send-secrets port secret-root
+ #:key (retry 60)
+ (handshake-timeout 120))
+ "Copy all files under SECRET-ROOT using TCP to secret-service listening at
+local PORT. If connect fails, sleep 1s and retry RETRY times; once connected,
+wait for at most HANDSHAKE-TIMEOUT seconds for handshake to complete. Return
+#f on failure."
(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) "sending secrets to ~a~%" port)
+ (define (send-files sock)
+ (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 (lambda (file)
+ (call-with-input-file file
+ (lambda (input)
+ (dump-port input sock))))
+ files)))
+
+ (log "sending secrets to ~a~%" port)
(let ((sock (socket AF_INET SOCK_STREAM 0))
(addr (make-socket-address AF_INET INADDR_LOOPBACK port)))
- ;; connect to wait for port
+ ;; Connect to QEMU on the forwarded port. The 'connect' call succeeds as
+ ;; soon as QEMU is ready, even if there's no server listening on the
+ ;; forward port inside the guest.
(let loop ((retry retry))
(catch 'system-error
(cute connect sock addr)
(lambda (key . args)
(when (zero? retry)
(apply throw key args))
- (format (current-error-port) "retrying connection~%")
+ (log "retrying connection [~a attempts left]~%"
+ (- retry 1))
(sleep 1)
(loop (1- retry)))))
- (format (current-error-port) "connected! sending files in ~s %~"
- secret-root)
- (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 dump-port <> sock)
- (cute open-input-file <>))
- files))))
+ (log "connected; waiting for handshake...~%")
+
+ ;; Wait for "hello" message from the server. This is the only way to know
+ ;; that we're really connected to the server inside the guest.
+ (match (select (list sock) '() '() handshake-timeout)
+ (((_) () ())
+ (match (read sock)
+ (('secret-service-server ('version version ...))
+ (log "sending files from ~s...~%" secret-root)
+ (send-files sock)
+ (log "done sending files to port ~a~%" port)
+ (close-port sock)
+ secret-root)
+ (x
+ (log "invalid handshake ~s~%" x)
+ (close-port sock)
+ #f)))
+ ((() () ()) ;timeout
+ (log "timeout while sending files to ~a~%" port)
+ (close-port sock)
+ #f))))
(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."
+Write them to the file system. Return the list of files installed on success,
+and #f otherwise."
(define (wait-for-client port)
;; Wait for a TCP connection on PORT. Note: We cannot use the
@@ -81,16 +123,26 @@ Write them to the file system."
(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~%"
+ (log "waiting for secrets on port ~a...~%" port)
+ (match (select (list sock) '() '() 60)
+ (((_) () ())
+ (match (accept sock)
+ ((client . address)
+ (log "client connection from ~a~%"
(inet-ntop (sockaddr:fam address)
(sockaddr:addr address)))
+
+ ;; Send a "hello" message. This allows the client running on the
+ ;; host to know that it's now actually connected to server running
+ ;; in the guest.
+ (write '(secret-service-server (version 0)) client)
+ (force-output client)
+ (close-port sock)
+ client)))
+ ((() () ())
+ (log "did not receive any secrets; time out~%")
(close-port sock)
- client))))
+ #f))))
;; TODO: Remove when (@ (guix build utils) dump-port) has a 'size'
;; parameter.
@@ -115,23 +167,24 @@ Write them to the file system."
(('secrets ('version 0)
('files ((files sizes modes) ...)))
(for-each (lambda (file size mode)
- (format (current-error-port)
- "installing file '~a' (~a bytes)...~%"
- file size)
+ (log "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))
+ files sizes modes)
+ (log "received ~a secret files~%" (length files))
+ files)
(_
- (format (current-error-port)
- "invalid secrets received~%")
+ (log "invalid secrets received~%")
#f)))
- (let* ((port (wait-for-client port))
- (result (read-secrets port)))
- (close-port port)
+ (let* ((port (wait-for-client port))
+ (result (and=> port read-secrets)))
+ (when port
+ (close-port port))
result))
;;; secret-service.scm ends here