Volume discovery and local storage management lib
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

316 lines
14KB

  1. # Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
  2. # All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. import binascii
  16. import mock
  17. from castellan.common.objects import symmetric_key as key
  18. from os_brick.encryptors import luks
  19. from os_brick.tests.encryptors import test_cryptsetup
  20. from oslo_concurrency import processutils as putils
  21. class LuksEncryptorTestCase(test_cryptsetup.CryptsetupEncryptorTestCase):
  22. def _create(self):
  23. return luks.LuksEncryptor(root_helper=self.root_helper,
  24. connection_info=self.connection_info,
  25. keymgr=self.keymgr)
  26. @mock.patch('os_brick.executor.Executor._execute')
  27. def test_is_luks(self, mock_execute):
  28. luks.is_luks(self.root_helper, self.dev_path, execute=mock_execute)
  29. mock_execute.assert_has_calls([
  30. mock.call('cryptsetup', 'isLuks', '--verbose', self.dev_path,
  31. run_as_root=True, root_helper=self.root_helper,
  32. check_exit_code=True),
  33. ], any_order=False)
  34. @mock.patch('os_brick.executor.Executor._execute')
  35. @mock.patch('os_brick.encryptors.luks.LOG')
  36. def test_is_luks_with_error(self, mock_log, mock_execute):
  37. error_msg = "Device %s is not a valid LUKS device." % self.dev_path
  38. mock_execute.side_effect = putils.ProcessExecutionError(
  39. exit_code=1, stderr=error_msg)
  40. luks.is_luks(self.root_helper, self.dev_path, execute=mock_execute)
  41. mock_execute.assert_has_calls([
  42. mock.call('cryptsetup', 'isLuks', '--verbose', self.dev_path,
  43. run_as_root=True, root_helper=self.root_helper,
  44. check_exit_code=True),
  45. ])
  46. self.assertEqual(1, mock_log.warning.call_count) # warning logged
  47. @mock.patch('os_brick.executor.Executor._execute')
  48. def test__format_volume(self, mock_execute):
  49. self.encryptor._format_volume("passphrase")
  50. mock_execute.assert_has_calls([
  51. mock.call('cryptsetup', '--batch-mode', 'luksFormat',
  52. '--type', 'luks1', '--key-file=-', self.dev_path,
  53. process_input='passphrase',
  54. root_helper=self.root_helper,
  55. run_as_root=True, check_exit_code=True, attempts=3),
  56. ])
  57. @mock.patch('os_brick.executor.Executor._execute')
  58. def test__open_volume(self, mock_execute):
  59. self.encryptor._open_volume("passphrase")
  60. mock_execute.assert_has_calls([
  61. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  62. self.dev_name, process_input='passphrase',
  63. root_helper=self.root_helper,
  64. run_as_root=True, check_exit_code=True),
  65. ])
  66. @mock.patch('os_brick.executor.Executor._execute')
  67. def test_attach_volume(self, mock_execute):
  68. fake_key = '0c84146034e747639b698368807286df'
  69. self.encryptor._get_key = mock.MagicMock()
  70. self.encryptor._get_key.return_value = (
  71. test_cryptsetup.fake__get_key(None, fake_key))
  72. self.encryptor.attach_volume(None)
  73. mock_execute.assert_has_calls([
  74. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  75. self.dev_name, process_input=fake_key,
  76. root_helper=self.root_helper,
  77. run_as_root=True, check_exit_code=True),
  78. mock.call('ln', '--symbolic', '--force',
  79. '/dev/mapper/%s' % self.dev_name, self.symlink_path,
  80. root_helper=self.root_helper,
  81. run_as_root=True, check_exit_code=True),
  82. ])
  83. @mock.patch('os_brick.executor.Executor._execute')
  84. def test_attach_volume_not_formatted(self, mock_execute):
  85. fake_key = 'bc37c5eccebe403f9cc2d0dd20dac2bc'
  86. self.encryptor._get_key = mock.MagicMock()
  87. self.encryptor._get_key.return_value = (
  88. test_cryptsetup.fake__get_key(None, fake_key))
  89. mock_execute.side_effect = [
  90. putils.ProcessExecutionError(exit_code=1), # luksOpen
  91. putils.ProcessExecutionError(exit_code=1), # isLuks
  92. mock.DEFAULT, # luksFormat
  93. mock.DEFAULT, # luksOpen
  94. mock.DEFAULT, # ln
  95. ]
  96. self.encryptor.attach_volume(None)
  97. mock_execute.assert_has_calls([
  98. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  99. self.dev_name, process_input=fake_key,
  100. root_helper=self.root_helper,
  101. run_as_root=True, check_exit_code=True),
  102. mock.call('cryptsetup', 'isLuks', '--verbose', self.dev_path,
  103. root_helper=self.root_helper,
  104. run_as_root=True, check_exit_code=True),
  105. mock.call('cryptsetup', '--batch-mode', 'luksFormat',
  106. '--type', 'luks1', '--key-file=-', self.dev_path,
  107. process_input=fake_key,
  108. root_helper=self.root_helper,
  109. run_as_root=True, check_exit_code=True, attempts=3),
  110. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  111. self.dev_name, process_input=fake_key,
  112. root_helper=self.root_helper,
  113. run_as_root=True, check_exit_code=True),
  114. mock.call('ln', '--symbolic', '--force',
  115. '/dev/mapper/%s' % self.dev_name, self.symlink_path,
  116. root_helper=self.root_helper,
  117. run_as_root=True, check_exit_code=True),
  118. ], any_order=False)
  119. @mock.patch('os_brick.executor.Executor._execute')
  120. def test_attach_volume_fail(self, mock_execute):
  121. fake_key = 'ea6c2e1b8f7f4f84ae3560116d659ba2'
  122. self.encryptor._get_key = mock.MagicMock()
  123. self.encryptor._get_key.return_value = (
  124. test_cryptsetup.fake__get_key(None, fake_key))
  125. mock_execute.side_effect = [
  126. putils.ProcessExecutionError(exit_code=1), # luksOpen
  127. mock.DEFAULT, # isLuks
  128. ]
  129. self.assertRaises(putils.ProcessExecutionError,
  130. self.encryptor.attach_volume, None)
  131. mock_execute.assert_has_calls([
  132. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  133. self.dev_name, process_input=fake_key,
  134. root_helper=self.root_helper,
  135. run_as_root=True, check_exit_code=True),
  136. mock.call('cryptsetup', 'isLuks', '--verbose', self.dev_path,
  137. root_helper=self.root_helper,
  138. run_as_root=True, check_exit_code=True),
  139. ], any_order=False)
  140. @mock.patch('os_brick.executor.Executor._execute')
  141. def test__close_volume(self, mock_execute):
  142. self.encryptor.detach_volume()
  143. mock_execute.assert_has_calls([
  144. mock.call('cryptsetup', 'luksClose', self.dev_name,
  145. root_helper=self.root_helper,
  146. attempts=3, run_as_root=True, check_exit_code=[0, 4]),
  147. ])
  148. @mock.patch('os_brick.executor.Executor._execute')
  149. def test_detach_volume(self, mock_execute):
  150. self.encryptor.detach_volume()
  151. mock_execute.assert_has_calls([
  152. mock.call('cryptsetup', 'luksClose', self.dev_name,
  153. root_helper=self.root_helper,
  154. attempts=3, run_as_root=True, check_exit_code=[0, 4]),
  155. ])
  156. def test_get_mangled_passphrase(self):
  157. # Confirm that a mangled passphrase is provided as per bug#1633518
  158. unmangled_raw_key = bytes(binascii.unhexlify('0725230b'))
  159. symmetric_key = key.SymmetricKey('AES', len(unmangled_raw_key) * 8,
  160. unmangled_raw_key)
  161. unmangled_encoded_key = symmetric_key.get_encoded()
  162. self.assertEqual(self.encryptor._get_mangled_passphrase(
  163. unmangled_encoded_key), '72523b')
  164. @mock.patch('os_brick.executor.Executor._execute')
  165. def test_attach_volume_unmangle_passphrase(self, mock_execute):
  166. fake_key = '0725230b'
  167. fake_key_mangled = '72523b'
  168. self.encryptor._get_key = mock.MagicMock()
  169. self.encryptor._get_key.return_value = \
  170. test_cryptsetup.fake__get_key(None, fake_key)
  171. mock_execute.side_effect = [
  172. putils.ProcessExecutionError(exit_code=2), # luksOpen
  173. mock.DEFAULT, # luksOpen
  174. mock.DEFAULT, # luksClose
  175. mock.DEFAULT, # luksAddKey
  176. mock.DEFAULT, # luksOpen
  177. mock.DEFAULT, # luksClose
  178. mock.DEFAULT, # luksRemoveKey
  179. mock.DEFAULT, # luksOpen
  180. mock.DEFAULT, # ln
  181. ]
  182. self.encryptor.attach_volume(None)
  183. mock_execute.assert_has_calls([
  184. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  185. self.dev_name, process_input=fake_key,
  186. root_helper=self.root_helper, run_as_root=True,
  187. check_exit_code=True),
  188. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  189. self.dev_name, process_input=fake_key_mangled,
  190. root_helper=self.root_helper, run_as_root=True,
  191. check_exit_code=True),
  192. mock.call('cryptsetup', 'luksClose', self.dev_name,
  193. root_helper=self.root_helper, run_as_root=True,
  194. check_exit_code=[0, 4], attempts=3),
  195. mock.call('cryptsetup', 'luksAddKey', self.dev_path,
  196. '--force-password',
  197. process_input=''.join([fake_key_mangled,
  198. '\n', fake_key,
  199. '\n', fake_key]),
  200. root_helper=self.root_helper, run_as_root=True,
  201. check_exit_code=True),
  202. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  203. self.dev_name, process_input=fake_key,
  204. root_helper=self.root_helper, run_as_root=True,
  205. check_exit_code=True),
  206. mock.call('cryptsetup', 'luksClose', self.dev_name,
  207. root_helper=self.root_helper, run_as_root=True,
  208. check_exit_code=[0, 4], attempts=3),
  209. mock.call('cryptsetup', 'luksRemoveKey', self.dev_path,
  210. process_input=fake_key_mangled,
  211. root_helper=self.root_helper, run_as_root=True,
  212. check_exit_code=True),
  213. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  214. self.dev_name, process_input=fake_key,
  215. root_helper=self.root_helper, run_as_root=True,
  216. check_exit_code=True),
  217. mock.call('ln', '--symbolic', '--force',
  218. '/dev/mapper/%s' % self.dev_name, self.symlink_path,
  219. root_helper=self.root_helper, run_as_root=True,
  220. check_exit_code=True),
  221. ], any_order=False)
  222. self.assertEqual(9, mock_execute.call_count)
  223. class Luks2EncryptorTestCase(LuksEncryptorTestCase):
  224. def _create(self):
  225. return luks.Luks2Encryptor(root_helper=self.root_helper,
  226. connection_info=self.connection_info,
  227. keymgr=self.keymgr)
  228. @mock.patch('os_brick.executor.Executor._execute')
  229. def test__format_volume(self, mock_execute):
  230. self.encryptor._format_volume("passphrase")
  231. mock_execute.assert_has_calls([
  232. mock.call('cryptsetup', '--batch-mode', 'luksFormat',
  233. '--type', 'luks2', '--key-file=-', self.dev_path,
  234. process_input='passphrase',
  235. root_helper=self.root_helper,
  236. run_as_root=True, check_exit_code=True, attempts=3),
  237. ])
  238. @mock.patch('os_brick.executor.Executor._execute')
  239. def test_attach_volume_not_formatted(self, mock_execute):
  240. fake_key = 'bc37c5eccebe403f9cc2d0dd20dac2bc'
  241. self.encryptor._get_key = mock.MagicMock()
  242. self.encryptor._get_key.return_value = (
  243. test_cryptsetup.fake__get_key(None, fake_key))
  244. mock_execute.side_effect = [
  245. putils.ProcessExecutionError(exit_code=1), # luksOpen
  246. putils.ProcessExecutionError(exit_code=1), # isLuks
  247. mock.DEFAULT, # luksFormat
  248. mock.DEFAULT, # luksOpen
  249. mock.DEFAULT, # ln
  250. ]
  251. self.encryptor.attach_volume(None)
  252. mock_execute.assert_has_calls([
  253. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  254. self.dev_name, process_input=fake_key,
  255. root_helper=self.root_helper,
  256. run_as_root=True, check_exit_code=True),
  257. mock.call('cryptsetup', 'isLuks', '--verbose', self.dev_path,
  258. root_helper=self.root_helper,
  259. run_as_root=True, check_exit_code=True),
  260. mock.call('cryptsetup', '--batch-mode', 'luksFormat',
  261. '--type', 'luks2', '--key-file=-', self.dev_path,
  262. process_input=fake_key,
  263. root_helper=self.root_helper,
  264. run_as_root=True, check_exit_code=True, attempts=3),
  265. mock.call('cryptsetup', 'luksOpen', '--key-file=-', self.dev_path,
  266. self.dev_name, process_input=fake_key,
  267. root_helper=self.root_helper,
  268. run_as_root=True, check_exit_code=True),
  269. mock.call('ln', '--symbolic', '--force',
  270. '/dev/mapper/%s' % self.dev_name, self.symlink_path,
  271. root_helper=self.root_helper,
  272. run_as_root=True, check_exit_code=True),
  273. ], any_order=False)