unofficial mirror of guix-patches@gnu.org 
 help / color / mirror / code / Atom feed
* [bug#38478] [PATCH 0/4] "guix deploy" authenticates SSH servers [security]
@ 2019-12-03 21:09 Ludovic Courtès
  2019-12-03 21:15 ` [bug#38478] [PATCH 1/4] ssh: Add 'authenticate-server*' and use it for offloading Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-03 21:09 UTC (permalink / raw)
  To: 38478; +Cc: Ludovic Courtès

Hi!

This series allow users to specify the remote host key in
<machine-ssh-configuration> used for “guix deploy”, so you
can have that under version control and entirely managed by
Guix, like “guix offload” does.

The second patch fixes a security issue: ‘open-ssh-session’ from
(guix ssh), which is used by “guix deploy” and support for
“GUIX_DAEMON_SOCKET=ssh://…” in (guix store ssh), would not
authenticate the server it’s talking to.

Feedback welcome!

Ludo’.

Ludovic Courtès (4):
  ssh: Add 'authenticate-server*' and use it for offloading.
  ssh: Always authenticate the server [security fix].
  ssh: 'open-ssh-session' can be passed the expected host key.
  machine: ssh: <machine-ssh-configuration> can include the host key.

 doc/guix.texi            | 12 +++++++
 gnu/machine/ssh.scm      |  9 ++++--
 guix/scripts/offload.scm | 30 ++---------------
 guix/ssh.scm             | 69 ++++++++++++++++++++++++++++++++++++++--
 4 files changed, 87 insertions(+), 33 deletions(-)

-- 
2.24.0

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 1/4] ssh: Add 'authenticate-server*' and use it for offloading.
  2019-12-03 21:09 [bug#38478] [PATCH 0/4] "guix deploy" authenticates SSH servers [security] Ludovic Courtès
@ 2019-12-03 21:15 ` Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 2/4] ssh: Always authenticate the server [security fix] Ludovic Courtès
                     ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-03 21:15 UTC (permalink / raw)
  To: 38478; +Cc: Ludovic Courtès

* guix/scripts/offload.scm (host-key->type+key): Remove.
(open-ssh-session): Replace server authentication code with a call to
'authenticate-server*'.
* guix/ssh.scm (host-key->type+key, authenticate-server*): New
procedures.
---
 guix/scripts/offload.scm | 30 ++----------------------------
 guix/ssh.scm             | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+), 28 deletions(-)

diff --git a/guix/scripts/offload.scm b/guix/scripts/offload.scm
index 18473684eb..e81b6c25f2 100644
--- a/guix/scripts/offload.scm
+++ b/guix/scripts/offload.scm
@@ -149,19 +149,6 @@ ignoring it~%")
          (leave (G_ "failed to load machine file '~a': ~s~%")
                 file args))))))
 
-(define (host-key->type+key host-key)
-  "Destructure HOST-KEY, an OpenSSH host key string, and return two values:
-its key type as a symbol, and the actual base64-encoded string."
-  (define (type->symbol type)
-    (and (string-prefix? "ssh-" type)
-         (string->symbol (string-drop type 4))))
-
-  (match (string-tokenize host-key)
-    ((type key x)
-     (values (type->symbol type) key))
-    ((type key)
-     (values (type->symbol type) key))))
-
 (define (private-key-from-file* file)
   "Like 'private-key-from-file', but raise an error that 'with-error-handling'
 can interpret meaningfully."
@@ -203,21 +190,8 @@ private key from '~a': ~a")
                                (build-machine-compression-level machine))))
     (match (connect! session)
       ('ok
-       ;; Authenticate the server.  XXX: Guile-SSH 0.10.1 doesn't know about
-       ;; ed25519 keys and 'get-key-type' returns #f in that case.
-       (let-values (((server)   (get-server-public-key session))
-                    ((type key) (host-key->type+key
-                                 (build-machine-host-key machine))))
-         (unless (and (or (not (get-key-type server))
-                          (eq? (get-key-type server) type))
-                      (string=? (public-key->string server) key))
-           ;; Key mismatch: something's wrong.  XXX: It could be that the server
-           ;; provided its Ed25519 key when we where expecting its RSA key.
-           (leave (G_ "server at '~a' returned host key '~a' of type '~a' \
-instead of '~a' of type '~a'~%")
-                  (build-machine-name machine)
-                  (public-key->string server) (get-key-type server)
-                  key type)))
+       ;; Make sure the server's key is what we expect.
+       (authenticate-server* session (build-machine-host-key machine))
 
        (let ((auth (userauth-public-key! session private)))
          (unless (eq? 'success auth)
diff --git a/guix/ssh.scm b/guix/ssh.scm
index 5fd3c280e8..f34e71392b 100644
--- a/guix/ssh.scm
+++ b/guix/ssh.scm
@@ -37,6 +37,8 @@
   #:use-module (ice-9 format)
   #:use-module (ice-9 binary-ports)
   #:export (open-ssh-session
+            authenticate-server*
+
             remote-inferior
             remote-daemon-channel
             connect-to-remote-daemon
@@ -60,6 +62,41 @@
 (define %compression
   "zlib@openssh.com,zlib")
 
+(define (host-key->type+key host-key)
+  "Destructure HOST-KEY, an OpenSSH host key string, and return two values:
+its key type as a symbol, and the actual base64-encoded string."
+  (define (type->symbol type)
+    (and (string-prefix? "ssh-" type)
+         (string->symbol (string-drop type 4))))
+
+  (match (string-tokenize host-key)
+    ((type key x)
+     (values (type->symbol type) key))
+    ((type key)
+     (values (type->symbol type) key))))
+
+(define (authenticate-server* session key)
+  "Make sure the server for SESSION has the given KEY, where KEY is a string
+such as \"ssh-ed25519 AAAAC3Nz… root@example.org\".  Raise an exception if the
+actual key does not match."
+  (let-values (((server)   (get-server-public-key session))
+               ((type key) (host-key->type+key key)))
+    (unless (and (or (not (get-key-type server))
+                     (eq? (get-key-type server) type))
+                 (string=? (public-key->string server) key))
+      ;; Key mismatch: something's wrong.  XXX: It could be that the server
+      ;; provided its Ed25519 key when we where expecting its RSA key.  XXX:
+      ;; Guile-SSH 0.10.1 doesn't know about ed25519 keys and 'get-key-type'
+      ;; returns #f in that case.
+      (raise (condition
+              (&message
+               (message (format #f (G_ "server at '~a' returned host key \
+'~a' of type '~a' instead of '~a' of type '~a'~%")
+                                (session-get session 'host)
+                                (public-key->string server)
+                                (get-key-type server)
+                                key type))))))))
+
 (define* (open-ssh-session host #:key user port identity
                            (compression %compression)
                            (timeout 3600))
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 2/4] ssh: Always authenticate the server [security fix].
  2019-12-03 21:15 ` [bug#38478] [PATCH 1/4] ssh: Add 'authenticate-server*' and use it for offloading Ludovic Courtès
@ 2019-12-03 21:15   ` Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 3/4] ssh: 'open-ssh-session' can be passed the expected host key Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the " Ludovic Courtès
  2 siblings, 0 replies; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-03 21:15 UTC (permalink / raw)
  To: 38478; +Cc: Ludovic Courtès

Until now, users of 'open-ssh-session', including "guix deploy" and
"GUIX_DAEMON_SOCKET=ssh://…" (but not "guix offload"), would not
authenticate the SSH server they're talking to.

* guix/ssh.scm (open-ssh-session): Call 'authenticate-server'.
---
 guix/ssh.scm | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/guix/ssh.scm b/guix/ssh.scm
index f34e71392b..519c723155 100644
--- a/guix/ssh.scm
+++ b/guix/ssh.scm
@@ -125,6 +125,17 @@ Throw an error on failure."
 
     (match (connect! session)
       ('ok
+       ;; Authenticate against ~/.ssh/known_hosts.
+       (match (authenticate-server session)
+         ('ok #f)
+         (reason
+          (raise (condition
+                  (&message
+                   (message (format #f (G_ "failed to authenticate \
+server at '~a': ~a")
+                                    (session-get session 'host)
+                                    reason)))))))
+
        ;; Use public key authentication, via the SSH agent if it's available.
        (match (userauth-public-key/auto! session)
          ('success
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 3/4] ssh: 'open-ssh-session' can be passed the expected host key.
  2019-12-03 21:15 ` [bug#38478] [PATCH 1/4] ssh: Add 'authenticate-server*' and use it for offloading Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 2/4] ssh: Always authenticate the server [security fix] Ludovic Courtès
@ 2019-12-03 21:15   ` Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the " Ludovic Courtès
  2 siblings, 0 replies; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-03 21:15 UTC (permalink / raw)
  To: 38478; +Cc: Ludovic Courtès

* guix/ssh.scm (open-ssh-session): Add #:host-key parameter.
Pass #:knownhosts to 'make-session'.  When HOST-KEY is true, call
'authenticate-server*' instead of 'authenticate-server'.
---
 guix/ssh.scm | 39 +++++++++++++++++++++++++++------------
 1 file changed, 27 insertions(+), 12 deletions(-)

diff --git a/guix/ssh.scm b/guix/ssh.scm
index 519c723155..291ce20b61 100644
--- a/guix/ssh.scm
+++ b/guix/ssh.scm
@@ -98,14 +98,20 @@ actual key does not match."
                                 key type))))))))
 
 (define* (open-ssh-session host #:key user port identity
+                           host-key
                            (compression %compression)
                            (timeout 3600))
   "Open an SSH session for HOST and return it.  IDENTITY specifies the file
 name of a private key to use for authenticating with the host.  When USER,
 PORT, or IDENTITY are #f, use default values or whatever '~/.ssh/config'
-specifies; otherwise use them.  Install TIMEOUT as the maximum time in seconds
-after which a read or write operation on a channel of the returned session is
-considered as failing.
+specifies; otherwise use them.
+
+When HOST-KEY is true, it must be a string like \"ssh-ed25519 AAAAC3Nz…
+root@example.org\"; the server is authenticated and an error is raised if its
+host key is different from HOST-KEY.
+
+Install TIMEOUT as the maximum time in seconds after which a read or write
+operation on a channel of the returned session is considered as failing.
 
 Throw an error on failure."
   (let ((session (make-session #:user user
@@ -115,6 +121,11 @@ Throw an error on failure."
                                #:timeout 10       ;seconds
                                ;; #:log-verbosity 'protocol
 
+                               ;; Prevent libssh from reading
+                               ;; ~/.ssh/known_hosts when the caller provides
+                               ;; a HOST-KEY to match against.
+                               #:knownhosts (and host-key "/dev/null")
+
                                ;; We need lightweight compression when
                                ;; exchanging full archives.
                                #:compression compression
@@ -125,16 +136,20 @@ Throw an error on failure."
 
     (match (connect! session)
       ('ok
-       ;; Authenticate against ~/.ssh/known_hosts.
-       (match (authenticate-server session)
-         ('ok #f)
-         (reason
-          (raise (condition
-                  (&message
-                   (message (format #f (G_ "failed to authenticate \
+       (if host-key
+           ;; Make sure the server's key is what we expect.
+           (authenticate-server* session host-key)
+
+           ;; Authenticate against ~/.ssh/known_hosts.
+           (match (authenticate-server session)
+             ('ok #f)
+             (reason
+              (raise (condition
+                      (&message
+                       (message (format #f (G_ "failed to authenticate \
 server at '~a': ~a")
-                                    (session-get session 'host)
-                                    reason)))))))
+                                        (session-get session 'host)
+                                        reason))))))))
 
        ;; Use public key authentication, via the SSH agent if it's available.
        (match (userauth-public-key/auto! session)
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the host key.
  2019-12-03 21:15 ` [bug#38478] [PATCH 1/4] ssh: Add 'authenticate-server*' and use it for offloading Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 2/4] ssh: Always authenticate the server [security fix] Ludovic Courtès
  2019-12-03 21:15   ` [bug#38478] [PATCH 3/4] ssh: 'open-ssh-session' can be passed the expected host key Ludovic Courtès
@ 2019-12-03 21:15   ` Ludovic Courtès
  2019-12-04 13:19     ` Jakob L. Kreuze
  2 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-03 21:15 UTC (permalink / raw)
  To: 38478; +Cc: Ludovic Courtès

* gnu/machine/ssh.scm (<machine-ssh-configuration>)[host-key]: New field.
(machine-ssh-session): Pass #:host-key to 'open-ssh-session'.
* doc/guix.texi (Invoking guix deploy): Document it.
---
 doc/guix.texi       | 12 ++++++++++++
 gnu/machine/ssh.scm |  9 +++++++--
 2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 2da1ecd64c..e6e015ad3e 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -26412,6 +26412,18 @@ keyring.
 @item @code{identity} (default: @code{#f})
 If specified, the path to the SSH private key to use to authenticate with the
 remote host.
+
+@item @code{host-key} (default: @code{#f})
+This should be the SSH host key of the machine, which looks like this:
+
+@example
+ssh-ed25519 AAAAC3Nz@dots{} root@@example.org
+@end example
+
+When @code{host-key} is @code{#f}, the server is authenticated against
+the @file{~/.ssh/known_hosts} file, just like the OpenSSH @command{ssh}
+client does.
+
 @end table
 @end deftp
 
diff --git a/gnu/machine/ssh.scm b/gnu/machine/ssh.scm
index 6e3ed0e092..23ae917b79 100644
--- a/gnu/machine/ssh.scm
+++ b/gnu/machine/ssh.scm
@@ -54,6 +54,7 @@
             machine-ssh-configuration-authorize?
             machine-ssh-configuration-port
             machine-ssh-configuration-user
+            machine-ssh-configuration-host-key
             machine-ssh-configuration-session))
 
 ;;; Commentary:
@@ -87,6 +88,8 @@
   (identity       machine-ssh-configuration-identity       ; path to a private key
                   (default #f))
   (session        machine-ssh-configuration-session        ; session
+                  (default #f))
+  (host-key       machine-ssh-configuration-host-key       ; #f | string
                   (default #f)))
 
 (define (machine-ssh-session machine)
@@ -98,11 +101,13 @@ one from the configuration's parameters if one was not provided."
         (let ((host-name (machine-ssh-configuration-host-name config))
               (user (machine-ssh-configuration-user config))
               (port (machine-ssh-configuration-port config))
-              (identity (machine-ssh-configuration-identity config)))
+              (identity (machine-ssh-configuration-identity config))
+              (host-key (machine-ssh-configuration-host-key config)))
           (open-ssh-session host-name
                             #:user user
                             #:port port
-                            #:identity identity)))))
+                            #:identity identity
+                            #:host-key host-key)))))
 
 \f
 ;;;
-- 
2.24.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the host key.
  2019-12-03 21:15   ` [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the " Ludovic Courtès
@ 2019-12-04 13:19     ` Jakob L. Kreuze
  2019-12-04 17:33       ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Jakob L. Kreuze @ 2019-12-04 13:19 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 38478

[-- Attachment #1: Type: text/plain, Size: 184 bytes --]

I've only been able to follow the updates to "guix deploy" somewhat
tangentially, but I was very excited to see this patch in my inbox.
Thumbs up from me, thanks Ludo!

Regards,
Jakob

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the host key.
  2019-12-04 13:19     ` Jakob L. Kreuze
@ 2019-12-04 17:33       ` Ludovic Courtès
  2019-12-06  0:50         ` Jakob L. Kreuze
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-04 17:33 UTC (permalink / raw)
  To: Jakob L. Kreuze; +Cc: 38478

Hi!

zerodaysfordays@sdf.lonestar.org (Jakob L. Kreuze) skribis:

> I've only been able to follow the updates to "guix deploy" somewhat
> tangentially, but I was very excited to see this patch in my inbox.
> Thumbs up from me, thanks Ludo!

Heheh, thank you!

I went ahead and pushed it as it seemed like a good idea to not wait.

BTW, I’m wondering if we should go further and deprecate missing/#f
‘host-key’ fields altogether.  WDYT?

To me it just seems wiser to have that info within the deploy config
rather than out-of-band in ~/.ssh/known_hosts.

Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the host key.
  2019-12-04 17:33       ` Ludovic Courtès
@ 2019-12-06  0:50         ` Jakob L. Kreuze
  2019-12-06 12:16           ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Jakob L. Kreuze @ 2019-12-06  0:50 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 38478

[-- Attachment #1: Type: text/plain, Size: 651 bytes --]

Ludovic Courtès <ludo@gnu.org> writes:

> I went ahead and pushed it as it seemed like a good idea to not wait.

Agreed :)

> BTW, I’m wondering if we should go further and deprecate missing/#f
> ‘host-key’ fields altogether.  WDYT?
>
> To me it just seems wiser to have that info within the deploy config
> rather than out-of-band in ~/.ssh/known_hosts.

I feel that's more in-line with the goals of Guix -- implicitly reading
~/.ssh/known_hosts doesn't seem declarative to me. What's our means for
deprecating features like that? A warning message when omitted? If
that's the case, I'm definitely on board.

Regards,
Jakob

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 832 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the host key.
  2019-12-06  0:50         ` Jakob L. Kreuze
@ 2019-12-06 12:16           ` Ludovic Courtès
  2019-12-07  0:04             ` Ludovic Courtès
  0 siblings, 1 reply; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-06 12:16 UTC (permalink / raw)
  To: Jakob L. Kreuze; +Cc: 38478

Hi!

zerodaysfordays@sdf.lonestar.org (Jakob L. Kreuze) skribis:

> Ludovic Courtès <ludo@gnu.org> writes:

[...]

>> BTW, I’m wondering if we should go further and deprecate missing/#f
>> ‘host-key’ fields altogether.  WDYT?
>>
>> To me it just seems wiser to have that info within the deploy config
>> rather than out-of-band in ~/.ssh/known_hosts.
>
> I feel that's more in-line with the goals of Guix -- implicitly reading
> ~/.ssh/known_hosts doesn't seem declarative to me. What's our means for
> deprecating features like that? A warning message when omitted? If
> that's the case, I'm definitely on board.

Yup, we can emit a deprecation warning when the key is #f.

So let’s take that route if nobody objects.  It’s easier to deprecate it
now that “guix deploy” is still very new.

Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the host key.
  2019-12-06 12:16           ` Ludovic Courtès
@ 2019-12-07  0:04             ` Ludovic Courtès
  0 siblings, 0 replies; 10+ messages in thread
From: Ludovic Courtès @ 2019-12-07  0:04 UTC (permalink / raw)
  To: Jakob L. Kreuze; +Cc: 38478

Ludovic Courtès <ludo@gnu.org> skribis:

> zerodaysfordays@sdf.lonestar.org (Jakob L. Kreuze) skribis:
>
>> Ludovic Courtès <ludo@gnu.org> writes:
>
> [...]
>
>>> BTW, I’m wondering if we should go further and deprecate missing/#f
>>> ‘host-key’ fields altogether.  WDYT?
>>>
>>> To me it just seems wiser to have that info within the deploy config
>>> rather than out-of-band in ~/.ssh/known_hosts.
>>
>> I feel that's more in-line with the goals of Guix -- implicitly reading
>> ~/.ssh/known_hosts doesn't seem declarative to me. What's our means for
>> deprecating features like that? A warning message when omitted? If
>> that's the case, I'm definitely on board.
>
> Yup, we can emit a deprecation warning when the key is #f.
>
> So let’s take that route if nobody objects.  It’s easier to deprecate it
> now that “guix deploy” is still very new.

Done in commit 2617d956d8ae122128a1ba2cc74983cbd683b042!

Ludo’.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2019-12-07  0:06 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-12-03 21:09 [bug#38478] [PATCH 0/4] "guix deploy" authenticates SSH servers [security] Ludovic Courtès
2019-12-03 21:15 ` [bug#38478] [PATCH 1/4] ssh: Add 'authenticate-server*' and use it for offloading Ludovic Courtès
2019-12-03 21:15   ` [bug#38478] [PATCH 2/4] ssh: Always authenticate the server [security fix] Ludovic Courtès
2019-12-03 21:15   ` [bug#38478] [PATCH 3/4] ssh: 'open-ssh-session' can be passed the expected host key Ludovic Courtès
2019-12-03 21:15   ` [bug#38478] [PATCH 4/4] machine: ssh: <machine-ssh-configuration> can include the " Ludovic Courtès
2019-12-04 13:19     ` Jakob L. Kreuze
2019-12-04 17:33       ` Ludovic Courtès
2019-12-06  0:50         ` Jakob L. Kreuze
2019-12-06 12:16           ` Ludovic Courtès
2019-12-07  0:04             ` Ludovic Courtès

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).