tests: dicod: Bail out if we cannot connect within 20 seconds.
[jackhill/guix/guix.git] / gnu / tests / ssh.scm
CommitLineData
d5b0c902 1;;; GNU Guix --- Functional package management for GNU
892d9089 2;;; Copyright © 2016, 2017 Ludovic Courtès <ludo@gnu.org>
cfaf4d11 3;;; Copyright © 2017 Clément Lassieur <clement@lassieur.org>
d5b0c902
LC
4;;;
5;;; This file is part of GNU Guix.
6;;;
7;;; GNU Guix is free software; you can redistribute it and/or modify it
8;;; under the terms of the GNU General Public License as published by
9;;; the Free Software Foundation; either version 3 of the License, or (at
10;;; your option) any later version.
11;;;
12;;; GNU Guix is distributed in the hope that it will be useful, but
13;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;;; GNU General Public License for more details.
16;;;
17;;; You should have received a copy of the GNU General Public License
18;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20(define-module (gnu tests ssh)
21 #:use-module (gnu tests)
22 #:use-module (gnu system)
d5b0c902
LC
23 #:use-module (gnu system vm)
24 #:use-module (gnu services)
d5b0c902
LC
25 #:use-module (gnu services ssh)
26 #:use-module (gnu services networking)
27 #:use-module (gnu packages ssh)
28 #:use-module (guix gexp)
29 #:use-module (guix store)
30 #:use-module (guix monads)
2b436389
LC
31 #:export (%test-openssh
32 %test-dropbear))
d5b0c902 33
36f666c6 34(define* (run-ssh-test name ssh-service pid-file #:key (sftp? #f))
0e598850
LC
35 "Run a test of an OS running SSH-SERVICE, which writes its PID to PID-FILE.
36SSH-SERVICE must be configured to listen on port 22 and to allow for root and
36f666c6
CL
37empty-password logins.
38
39When SFTP? is true, run an SFTP server test."
d5b0c902 40 (mlet* %store-monad ((os -> (marionette-operating-system
892d9089
LC
41 (simple-operating-system
42 (dhcp-client-service)
43 ssh-service)
d5b0c902
LC
44 #:imported-modules '((gnu services herd)
45 (guix combinators))))
46 (command (system-qemu-image/shared-store-script
47 os #:graphic? #f)))
48 (define test
49 (with-imported-modules '((gnu build marionette))
50 #~(begin
51 (eval-when (expand load eval)
52 ;; Prepare to use Guile-SSH.
53 (set! %load-path
54 (cons (string-append #$guile-ssh "/share/guile/site/"
55 (effective-version))
56 %load-path)))
57
58 (use-modules (gnu build marionette)
2b436389 59 (srfi srfi-26)
d5b0c902
LC
60 (srfi srfi-64)
61 (ice-9 match)
62 (ssh session)
63 (ssh auth)
36f666c6
CL
64 (ssh channel)
65 (ssh sftp))
d5b0c902
LC
66
67 (define marionette
68 ;; Enable TCP forwarding of the guest's port 22.
69 (make-marionette (list #$command "-net"
70 "user,hostfwd=tcp::2222-:22")))
71
72 (define (wait-for-file file)
73 ;; Wait until FILE exists in the guest; 'read' its content and
74 ;; return it.
75 (marionette-eval
76 `(let loop ((i 10))
77 (cond ((file-exists? ,file)
78 (call-with-input-file ,file read))
79 ((> i 0)
80 (sleep 1)
81 (loop (- i 1)))
82 (else
83 (error "file didn't show up" ,file))))
84 marionette))
85
cfaf4d11
CL
86 (define (make-session-for-test)
87 "Make a session with predefined parameters for a test."
88 (make-session #:user "root"
89 #:port 2222
90 #:host "localhost"
91 #:log-verbosity 'protocol))
92
93 (define (call-with-connected-session proc)
94 "Call the one-argument procedure PROC with a freshly created and
95connected SSH session object, return the result of the procedure call. The
96session is disconnected when the PROC is finished."
97 (let ((session (make-session-for-test)))
98 (dynamic-wind
99 (lambda ()
100 (let ((result (connect! session)))
101 (unless (equal? result 'ok)
102 (error "Could not connect to a server"
103 session result))))
104 (lambda () (proc session))
105 (lambda () (disconnect! session)))))
106
107 (define (call-with-connected-session/auth proc)
108 "Make an authenticated session. We should be able to connect as
109root with an empty password."
110 (call-with-connected-session
111 (lambda (session)
112 ;; Try the simple authentication methods. Dropbear requires
113 ;; 'none' when there are no passwords, whereas OpenSSH accepts
114 ;; 'password' with an empty password.
115 (let loop ((methods (list (cut userauth-password! <> "")
116 (cut userauth-none! <>))))
117 (match methods
118 (()
119 (error "all the authentication methods failed"))
120 ((auth rest ...)
121 (match (pk 'auth (auth session))
122 ('success
123 (proc session))
124 ('denied
125 (loop rest)))))))))
126
d5b0c902
LC
127 (mkdir #$output)
128 (chdir #$output)
129
0e598850 130 (test-begin "ssh-daemon")
d5b0c902
LC
131
132 ;; Wait for sshd to be up and running.
133 (test-eq "service running"
134 'running!
135 (marionette-eval
136 '(begin
137 (use-modules (gnu services herd))
138 (start-service 'ssh-daemon)
139 'running!)
140 marionette))
141
142 ;; Check sshd's PID file.
143 (test-equal "sshd PID"
0e598850 144 (wait-for-file #$pid-file)
d5b0c902
LC
145 (marionette-eval
146 '(begin
147 (use-modules (gnu services herd)
148 (srfi srfi-1))
149
150 (live-service-running
151 (find (lambda (live)
152 (memq 'ssh-daemon
153 (live-service-provision live)))
154 (current-services))))
155 marionette))
156
cfaf4d11 157 ;; Connect to the guest over SSH. Make sure we can run a shell
d5b0c902 158 ;; command there.
cfaf4d11 159 (test-equal "shell command"
d5b0c902 160 'hello
cfaf4d11
CL
161 (call-with-connected-session/auth
162 (lambda (session)
163 ;; FIXME: 'get-server-public-key' segfaults.
164 ;; (get-server-public-key session)
165 (let ((channel (make-channel session)))
166 (channel-open-session channel)
167 (channel-request-exec channel "echo hello > /root/witness")
168 (and (zero? (channel-get-exit-status channel))
169 (wait-for-file "/root/witness"))))))
d5b0c902 170
36f666c6
CL
171 ;; Connect to the guest over SFTP. Make sure we can write and
172 ;; read a file there.
173 (unless #$sftp?
174 (test-skip 1))
175 (test-equal "SFTP file writing and reading"
176 'hello
177 (call-with-connected-session/auth
178 (lambda (session)
179 (let ((sftp-session (make-sftp-session session))
180 (witness "/root/sftp-witness"))
181 (call-with-remote-output-file sftp-session witness
182 (cut display "hello" <>))
183 (call-with-remote-input-file sftp-session witness
184 read)))))
185
d5b0c902
LC
186 (test-end)
187 (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
188
189 (gexp->derivation name test)))
190
191(define %test-openssh
192 (system-test
193 (name "openssh")
194 (description "Connect to a running OpenSSH daemon.")
0e598850
LC
195 (value (run-ssh-test name
196 ;; Allow root logins with an empty password to
197 ;; simplify testing.
198 (service openssh-service-type
199 (openssh-configuration
200 (permit-root-login #t)
201 (allow-empty-passwords? #t)))
36f666c6
CL
202 "/var/run/sshd.pid"
203 #:sftp? #t))))
2b436389
LC
204
205(define %test-dropbear
206 (system-test
207 (name "dropbear")
208 (description "Connect to a running Dropbear SSH daemon.")
209 (value (run-ssh-test name
210 (service dropbear-service-type
211 (dropbear-configuration
212 (root-login? #t)
213 (allow-empty-passwords? #t)))
214 "/var/run/dropbear.pid"))))