1#!/usr/bin/env python3
2#
3# Test ssh image creation
4#
5# Copyright (C) 2018 Red Hat, Inc.
6#
7# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program.  If not, see <http://www.gnu.org/licenses/>.
21#
22
23import iotests
24import subprocess
25import re
26
27iotests.verify_image_format(supported_fmts=['raw'])
28iotests.verify_protocol(supported=['ssh'])
29
30def filter_hash(qmsg):
31    def _filter(key, value):
32        if key == 'hash' and re.match('[0-9a-f]+', value):
33            return 'HASH'
34        return value
35    return iotests.filter_qmp(qmsg, _filter)
36
37def blockdev_create(vm, options):
38    vm.blockdev_create(options, filters=[iotests.filter_qmp_testfiles, filter_hash])
39
40with iotests.FilePath('t.img') as disk_path, \
41     iotests.VM() as vm:
42
43    remote_path = iotests.remote_filename(disk_path)
44
45    #
46    # Successful image creation (defaults)
47    #
48    iotests.log("=== Successful image creation (defaults) ===")
49    iotests.log("")
50
51    vm.launch()
52    blockdev_create(vm, { 'driver': 'ssh',
53                          'location': {
54                              'path': disk_path,
55                              'server': {
56                                  'host': '127.0.0.1',
57                                  'port': '22'
58                              }
59                          },
60                          'size': 4194304 })
61    vm.shutdown()
62
63    iotests.img_info_log(remote_path)
64    iotests.log("")
65    iotests.img_info_log(disk_path)
66
67    #
68    # Test host-key-check options
69    #
70    iotests.log("=== Test host-key-check options ===")
71    iotests.log("")
72
73    vm.launch()
74    blockdev_create(vm, { 'driver': 'ssh',
75                          'location': {
76                              'path': disk_path,
77                              'server': {
78                                  'host': '127.0.0.1',
79                                  'port': '22'
80                              },
81                              'host-key-check': {
82                                  'mode': 'none'
83                              }
84                          },
85                          'size': 8388608 })
86    vm.shutdown()
87
88    iotests.img_info_log(remote_path)
89
90    vm.launch()
91    blockdev_create(vm, { 'driver': 'ssh',
92                          'location': {
93                              'path': disk_path,
94                              'server': {
95                                  'host': '127.0.0.1',
96                                  'port': '22'
97                              },
98                              'host-key-check': {
99                                  'mode': 'known_hosts'
100                              }
101                          },
102                          'size': 4194304 })
103    vm.shutdown()
104
105    iotests.img_info_log(remote_path)
106
107    keys = subprocess.check_output(
108        'ssh-keyscan 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' +
109        'cut -d" " -f3',
110        shell=True).rstrip().decode('ascii').split('\n')
111
112    # Mappings of base64 representations to digests
113    md5_keys = {}
114    sha1_keys = {}
115
116    for key in keys:
117        md5_keys[key] = subprocess.check_output(
118            'echo %s | base64 -d | md5sum -b | cut -d" " -f1' % key,
119            shell=True).rstrip().decode('ascii')
120
121        sha1_keys[key] = subprocess.check_output(
122            'echo %s | base64 -d | sha1sum -b | cut -d" " -f1' % key,
123            shell=True).rstrip().decode('ascii')
124
125    vm.launch()
126
127    # Find correct key first
128    matching_key = None
129    for key in keys:
130        result = vm.qmp('blockdev-add',
131                        driver='ssh', node_name='node0', path=disk_path,
132                        server={
133                             'host': '127.0.0.1',
134                             'port': '22',
135                        }, host_key_check={
136                             'mode': 'hash',
137                             'type': 'md5',
138                             'hash': md5_keys[key],
139                        })
140
141        if 'error' not in result:
142            vm.qmp('blockdev-del', node_name='node0')
143            matching_key = key
144            break
145
146    if matching_key is None:
147        vm.shutdown()
148        iotests.notrun('Did not find a key that fits 127.0.0.1')
149
150    blockdev_create(vm, { 'driver': 'ssh',
151                          'location': {
152                              'path': disk_path,
153                              'server': {
154                                  'host': '127.0.0.1',
155                                  'port': '22'
156                              },
157                              'host-key-check': {
158                                  'mode': 'hash',
159                                  'type': 'md5',
160                                  'hash': 'wrong',
161                              }
162                          },
163                          'size': 2097152 })
164    blockdev_create(vm, { 'driver': 'ssh',
165                          'location': {
166                              'path': disk_path,
167                              'server': {
168                                  'host': '127.0.0.1',
169                                  'port': '22'
170                              },
171                              'host-key-check': {
172                                  'mode': 'hash',
173                                  'type': 'md5',
174                                  'hash': md5_keys[matching_key],
175                              }
176                          },
177                          'size': 8388608 })
178    vm.shutdown()
179
180    iotests.img_info_log(remote_path)
181
182    vm.launch()
183    blockdev_create(vm, { 'driver': 'ssh',
184                          'location': {
185                              'path': disk_path,
186                              'server': {
187                                  'host': '127.0.0.1',
188                                  'port': '22'
189                              },
190                              'host-key-check': {
191                                  'mode': 'hash',
192                                  'type': 'sha1',
193                                  'hash': 'wrong',
194                              }
195                          },
196                          'size': 2097152 })
197    blockdev_create(vm, { 'driver': 'ssh',
198                          'location': {
199                              'path': disk_path,
200                              'server': {
201                                  'host': '127.0.0.1',
202                                  'port': '22'
203                              },
204                              'host-key-check': {
205                                  'mode': 'hash',
206                                  'type': 'sha1',
207                                  'hash': sha1_keys[matching_key],
208                              }
209                          },
210                          'size': 4194304 })
211    vm.shutdown()
212
213    iotests.img_info_log(remote_path)
214
215    #
216    # Invalid path and user
217    #
218    iotests.log("=== Invalid path and user ===")
219    iotests.log("")
220
221    vm.launch()
222    blockdev_create(vm, { 'driver': 'ssh',
223                          'location': {
224                              'path': '/this/is/not/an/existing/path',
225                              'server': {
226                                  'host': '127.0.0.1',
227                                  'port': '22'
228                              },
229                              'host-key-check': {
230                                  'mode': 'none'
231                              }
232                          },
233                          'size': 4194304 })
234    blockdev_create(vm, { 'driver': 'ssh',
235                          'location': {
236                              'path': disk_path,
237                              'user': 'invalid user',
238                              'server': {
239                                  'host': '127.0.0.1',
240                                  'port': '22'
241                              },
242                              'host-key-check': {
243                                  'mode': 'none'
244                              }
245                          },
246                          'size': 4194304 })
247    vm.shutdown()
248