test_pillar.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Erik Johnson <erik@saltstack.com>
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import
  7. import copy
  8. import errno
  9. import logging
  10. import os
  11. import shutil
  12. import textwrap
  13. import subprocess
  14. # Import Salt Testing libs
  15. from tests.support.case import ModuleCase
  16. from tests.support.paths import TMP, TMP_CONF_DIR
  17. from tests.support.unit import skipIf
  18. from tests.support.helpers import requires_system_grains
  19. # Import salt libs
  20. import salt.utils.files
  21. import salt.utils.path
  22. import salt.utils.stringutils
  23. import salt.utils.yaml
  24. import salt.pillar as pillar
  25. log = logging.getLogger(__name__)
  26. GPG_HOMEDIR = os.path.join(TMP_CONF_DIR, 'gpgkeys')
  27. PILLAR_BASE = os.path.join(TMP, 'test-decrypt-pillar', 'pillar')
  28. TOP_SLS = os.path.join(PILLAR_BASE, 'top.sls')
  29. GPG_SLS = os.path.join(PILLAR_BASE, 'gpg.sls')
  30. DEFAULT_OPTS = {
  31. 'cachedir': os.path.join(TMP, 'rootdir', 'cache'),
  32. 'config_dir': TMP_CONF_DIR,
  33. 'optimization_order': [0, 1, 2],
  34. 'extension_modules': os.path.join(TMP,
  35. 'test-decrypt-pillar',
  36. 'extmods'),
  37. 'pillar_roots': {'base': [PILLAR_BASE]},
  38. 'ext_pillar_first': False,
  39. 'ext_pillar': [],
  40. 'decrypt_pillar_default': 'gpg',
  41. 'decrypt_pillar_delimiter': ':',
  42. 'decrypt_pillar_renderers': ['gpg'],
  43. }
  44. ADDITIONAL_OPTS = (
  45. 'conf_file',
  46. 'file_roots',
  47. 'state_top',
  48. 'renderer',
  49. 'renderer_whitelist',
  50. 'renderer_blacklist',
  51. )
  52. TEST_KEY = '''\
  53. -----BEGIN PGP PRIVATE KEY BLOCK-----
  54. lQOYBFiKrcYBCADAj92+fz20uKxxH0ffMwcryGG9IogkiUi2QrNYilB4hwrY5Qt7
  55. Sbywlk/mSDMcABxMxS0vegqc5pgglvAnsi9w7j//9nfjiirsyiTYOOD1akTFQr7b
  56. qT6zuGFA4oYmYHvfBOena485qvlyitYLKYT9h27TDiiH6Jgt4xSRbjeyhTf3/fKD
  57. JzHA9ii5oeVi1pH/8/4USgXanBdKwO0JKQtci+PF0qe/nkzRswqTIkdgx1oyNUqL
  58. tYJ0XPOy+UyOC4J4QDIt9PQbAmiur8By4g2lLYWlGOCjs7Fcj3n5meWKzf1pmXoY
  59. lAnSab8kUZSSkoWQoTO7RbjFypULKCZui45/ABEBAAEAB/wM1wsAMtfYfx/wgxd1
  60. yJ9HyhrKU80kMotIq/Xth3uKLecJQ2yakfYlCEDXqCTQTymT7OnwaoDeqXmnYqks
  61. 3HLRYvGdjb+8ym/GTkxapqBJfQaM6MB1QTnPHhJOE0zCrlhULK2NulxYihAMFTnk
  62. kKYviaJYLG+DcH0FQkkS0XihTKcqnsoJiS6iNd5SME3pa0qijR0D5f78fkvNzzEE
  63. 9vgAX1TgQ5PDJGN6nYlW2bWxTcg+FR2cUAQPTiP9wXCH6VyJoQay7KHVr3r/7SsU
  64. 89otfcx5HVDYPrez6xnP6wN0P/mKxCDbkERLDjZjWOmNXg2zn+/t3u02e+ybfAIp
  65. kTTxBADY/FmPgLpJ2bpcPH141twpHwhKIbENlTB9745Qknr6aLA0QVCkz49/3joO
  66. Sj+SZ7Jhl6cfbynrfHwX3b1bOFTzBUH2Tsi0HX40PezEFH0apf55FLZuMOBt/lc1
  67. ET6evpIHF0dcM+BvZa7E7MyTyEq8S7Cc9RoJyfeGbS7MG5FfuwQA4y9QOb/OQglq
  68. ZffkVItwY52RKWb/b2WQmt+IcVax/j7DmBva765SIfPDvOCMrYhJBI/uYHQ0Zia7
  69. SnC9+ez55wdYqgHkYojc21CIOnUvsPSj+rOpryoXzmcTuvKeVIyIA0h/mQyWjimR
  70. ENrikC4+O8GBMY6V4uvS4EFhLfHE9g0D/20lNOKkpAKPenr8iAPWcl0/pijJCGxF
  71. agnT7O2GQ9Lr5hSjW86agkevbGktu2ja5t/fHq0wpLQ4DVLMrR0/poaprTr307kW
  72. AlQV3z/C2cMHNysz4ulOgQrudQbhUEz2A8nQxRtIfWunkEugKLr1QiCkE1LJW8Np
  73. ZLxE6Qp0/KzdQva0HVNhbHQgR1BHIDxlcmlrQHNhbHRzdGFjay5jb20+iQFUBBMB
  74. CAA+FiEE+AxQ1ELHGEyFTZPYw5x3k9EbHGsFAliKrcYCGwMFCQPCZwAFCwkIBwIG
  75. FQgJCgsCBBYCAwECHgECF4AACgkQw5x3k9EbHGubUAf+PLdp1oTLVokockZgLyIQ
  76. wxOd3ofNOgNk4QoAkSMNSbtnYoQFKumRw/yGyPSIoHMsOC/ga98r8TAJEKfx3DLA
  77. rsD34oMAaYUT+XUd0KoSmlHqBrtDD1+eBASKYsCosHpCiKuQFfLKSxvpEr2YyL8L
  78. X3Q2TY5zFlGA9Eeq5g+rlb++yRZrruFN28EWtY/pyXFZgIB30ReDwPkM9hrioPZM
  79. 0Qf3+dWZSK1rWViclB51oNy4un9stTiFZptAqz4NTNssU5A4AcNQPwBwnKIYoE58
  80. Y/Zyv8HzILGykT+qFebqRlRBI/13eHdzgJOL1iPRfjTk5Cvr+vcyIxAklXOP81ja
  81. B50DmARYiq3GAQgArnzu4SPCCQGNcCNxN4QlMP5TNvRsm5KrPbcO9j8HPfB+DRXs
  82. 6B3mnuR6OJg7YuC0C2A/m2dSHJKkF0f2AwFRpxLjJ2iAFbrZAW/N0vZDx8zO+YAU
  83. HyLu0V04wdCE5DTLkgfWNR+0uMa8qZ4Kn56Gv7O+OFE7zgTHeZ7psWlxdafeW7u6
  84. zlC/3DWksNtuNb0vQDNMM4vgXbnORIfXdyh41zvEEnr/rKw8DuJAmo20mcv6Qi51
  85. PqqyM62ddQOEVfiMs9l4vmwZAjGFNFNInyPXnogL6UPCDmizb6hh8aX/MwG/XFIG
  86. KMJWbAVGpyBuqljKIt3qLu/s8ouPqkEN+f+nGwARAQABAAf+NA36d/kieGxZpTQ1
  87. oQHP1Jty+OiXhBwP8SPtF0J7ZxuZh07cs+zDsfBok/y6bsepfuFSaIq84OBQis+B
  88. kajxkp3cXZPb7l+lQLv5k++7Dd7Ien+ewSE7TQN6HLwYATrM5n5nBcc1M5C6lQGc
  89. mr0A5yz42TVG2bHsTpi9kBtsaVRSPUHSh8A8T6eOyCrT+/CAJVEEf7JyNyaqH1dy
  90. LuxI1VF3ySDEtFzuwN8EZQP9Yz/4AVyEQEA7WkNEwSQsBi2bWgWEdG+qjqnL+YKa
  91. vwe7/aJYPeL1zICnP/Osd/UcpDxR78MbozstbRljML0fTLj7UJ+XDazwv+Kl0193
  92. 2ZK2QQQAwgXvS19MYNkHO7kbNVLt1VE2ll901iC9GFHBpFUam6gmoHXpCarB+ShH
  93. 8x25aoUu4MxHmFxXd+Zq3d6q2yb57doWoPgvqcefpGmigaITnb1jhV2rt65V8deA
  94. SQazZNqBEBbZNIhfn6ObxHXXvaYaqq/UOEQ7uKyR9WMJT/rmqMEEAOY5h1R1t7AB
  95. JZ5VnhyAhdsNWw1gTcXB3o8gKz4vjdnPm0F4aVIPfB3BukETDc3sc2tKmCfUF7I7
  96. oOrh7iRez5F0RIC3KDzXF8qUuWBfPViww45JgftdKsecCIlEEYCoc+3goX0su2bP
  97. V1MDuHijMGTJCBABDgizNb0oynW5xcrbA/0QnKfpTwi7G3oRcJWv2YebVDRcU+SP
  98. dOYhq6SnmWPizEIljRG/X7FHJB+W7tzryO3sCDTAYwxFrfMwvJ2PwnAYI4349zYd
  99. lC28HowUkBYNhwBXc48xCfyhPZtD0aLx/OX1oLZ/vi8gd8TusgGupV/JjkFVO+Nd
  100. +shN/UEAldwqkkY2iQE8BBgBCAAmFiEE+AxQ1ELHGEyFTZPYw5x3k9EbHGsFAliK
  101. rcYCGwwFCQPCZwAACgkQw5x3k9EbHGu4wwf/dRFat91BRX1TJfwJl5otoAXpItYM
  102. 6kdWWf1Eb1BicAvXhI078MSH4WXdKkJjJr1fFP8Ynil513H4Mzb0rotMAhb0jLSA
  103. lSRkMbhMvPxoS2kaYzioaBpp8yXpGiNo7dF+PJXSm/Uwp3AkcFjoVbBOqDWGgxMi
  104. DvDAstzLZ9dIcmr+OmcRQykKOKXlhEl3HnR5CyuPrA8hdVup4oeVwdkJhfJFKLLb
  105. 3fR26wxJOmIOAt24eAUy721WfQ9txNAmhdy8mY842ODZESw6WatrQjRfuqosDgrk
  106. jc0cCHsEqJNZ2AB+1uEl3tcH0tyAFJa33F0znSonP17SS1Ff9sgHYBVLUg==
  107. =06Tz
  108. -----END PGP PRIVATE KEY BLOCK-----
  109. '''
  110. GPG_PILLAR_YAML = '''\
  111. secrets:
  112. vault:
  113. foo: |
  114. -----BEGIN PGP MESSAGE-----
  115. hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th
  116. W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74
  117. ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7
  118. +KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb
  119. VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73
  120. zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06
  121. KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh
  122. iFndxegN9w==
  123. =bAuo
  124. -----END PGP MESSAGE-----
  125. bar: this was unencrypted already
  126. baz: |
  127. -----BEGIN PGP MESSAGE-----
  128. hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz
  129. gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf
  130. 9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7
  131. Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2
  132. q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V
  133. kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl
  134. JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY
  135. 1OZi
  136. =7epf
  137. -----END PGP MESSAGE-----
  138. qux:
  139. - foo
  140. - bar
  141. - |
  142. -----BEGIN PGP MESSAGE-----
  143. hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS
  144. ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI
  145. gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA
  146. YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF
  147. f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE
  148. uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd
  149. 4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=
  150. =LrCQ
  151. -----END PGP MESSAGE-----
  152. '''
  153. GPG_PILLAR_ENCRYPTED = {
  154. 'secrets': {
  155. 'vault': {
  156. 'foo': '-----BEGIN PGP MESSAGE-----\n\nhQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th\nW1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74\nZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7\n+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb\nVMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73\nzBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06\nKMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh\niFndxegN9w==\n=bAuo\n-----END PGP MESSAGE-----\n',
  157. 'bar': 'this was unencrypted already',
  158. 'baz': '-----BEGIN PGP MESSAGE-----\n\nhQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz\ngLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf\n9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7\nIzwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2\nq+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V\nkJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl\nJkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY\n1OZi\n=7epf\n-----END PGP MESSAGE-----\n',
  159. 'qux': [
  160. 'foo',
  161. 'bar',
  162. '-----BEGIN PGP MESSAGE-----\n\nhQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS\nksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI\ngR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA\nYrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF\nf7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE\nuZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd\n4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=\n=LrCQ\n-----END PGP MESSAGE-----\n'
  163. ],
  164. },
  165. },
  166. }
  167. GPG_PILLAR_DECRYPTED = {
  168. 'secrets': {
  169. 'vault': {
  170. 'foo': 'supersecret',
  171. 'bar': 'this was unencrypted already',
  172. 'baz': 'rosebud',
  173. 'qux': ['foo', 'bar', 'baz'],
  174. },
  175. },
  176. }
  177. @skipIf(not salt.utils.path.which('gpg'), 'GPG is not installed')
  178. class DecryptGPGPillarTest(ModuleCase):
  179. '''
  180. Tests for pillar decryption
  181. '''
  182. maxDiff = None
  183. @classmethod
  184. def setUpClass(cls):
  185. try:
  186. os.makedirs(GPG_HOMEDIR, mode=0o700)
  187. except Exception:
  188. cls.created_gpg_homedir = False
  189. raise
  190. else:
  191. cls.created_gpg_homedir = True
  192. cmd_prefix = ['gpg', '--homedir', GPG_HOMEDIR]
  193. cmd = cmd_prefix + ['--list-keys']
  194. log.debug('Instantiating gpg keyring using: %s', cmd)
  195. output = subprocess.Popen(cmd,
  196. stdout=subprocess.PIPE,
  197. stderr=subprocess.STDOUT,
  198. shell=False).communicate()[0]
  199. log.debug('Result:\n%s', output)
  200. cmd = cmd_prefix + ['--import', '--allow-secret-key-import']
  201. log.debug('Importing keypair using: %s', cmd)
  202. output = subprocess.Popen(cmd,
  203. stdin=subprocess.PIPE,
  204. stdout=subprocess.PIPE,
  205. stderr=subprocess.STDOUT,
  206. shell=False).communicate(input=salt.utils.stringutils.to_bytes(TEST_KEY))[0]
  207. log.debug('Result:\n%s', output)
  208. os.makedirs(PILLAR_BASE)
  209. with salt.utils.files.fopen(TOP_SLS, 'w') as fp_:
  210. fp_.write(textwrap.dedent('''\
  211. base:
  212. '*':
  213. - gpg
  214. '''))
  215. with salt.utils.files.fopen(GPG_SLS, 'w') as fp_:
  216. fp_.write(GPG_PILLAR_YAML)
  217. @classmethod
  218. def tearDownClass(cls):
  219. cmd = ['gpg-connect-agent', '--homedir', GPG_HOMEDIR]
  220. try:
  221. log.debug('Killing gpg-agent using: %s', cmd)
  222. output = subprocess.Popen(cmd,
  223. stdin=subprocess.PIPE,
  224. stdout=subprocess.PIPE,
  225. stderr=subprocess.STDOUT,
  226. shell=False).communicate(input=b'KILLAGENT')[0]
  227. log.debug('Result:\n%s', output)
  228. except OSError:
  229. log.debug('No need to kill: old gnupg doesn\'t start the agent.')
  230. if cls.created_gpg_homedir:
  231. try:
  232. shutil.rmtree(GPG_HOMEDIR)
  233. except OSError as exc:
  234. # GPG socket can disappear before rmtree gets to this point
  235. if exc.errno != errno.ENOENT:
  236. raise
  237. shutil.rmtree(PILLAR_BASE)
  238. def _build_opts(self, opts):
  239. ret = copy.deepcopy(DEFAULT_OPTS)
  240. for item in ADDITIONAL_OPTS:
  241. ret[item] = self.master_opts[item]
  242. ret.update(opts)
  243. return ret
  244. @requires_system_grains
  245. def test_decrypt_pillar_default_renderer(self, grains=None):
  246. '''
  247. Test recursive decryption of secrets:vault as well as the fallback to
  248. default decryption renderer.
  249. '''
  250. decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\
  251. decrypt_pillar:
  252. - 'secrets:vault'
  253. '''))
  254. opts = self._build_opts(decrypt_pillar_opts)
  255. pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
  256. ret = pillar_obj.compile_pillar()
  257. self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
  258. @requires_system_grains
  259. def test_decrypt_pillar_alternate_delimiter(self, grains=None):
  260. '''
  261. Test recursive decryption of secrets:vault using a pipe instead of a
  262. colon as the nesting delimiter.
  263. '''
  264. decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\
  265. decrypt_pillar_delimiter: '|'
  266. decrypt_pillar:
  267. - 'secrets|vault'
  268. '''))
  269. opts = self._build_opts(decrypt_pillar_opts)
  270. pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
  271. ret = pillar_obj.compile_pillar()
  272. self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
  273. @requires_system_grains
  274. def test_decrypt_pillar_deeper_nesting(self, grains=None):
  275. '''
  276. Test recursive decryption, only with a more deeply-nested target. This
  277. should leave the other keys in secrets:vault encrypted.
  278. '''
  279. decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\
  280. decrypt_pillar:
  281. - 'secrets:vault:qux'
  282. '''))
  283. opts = self._build_opts(decrypt_pillar_opts)
  284. pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
  285. ret = pillar_obj.compile_pillar()
  286. expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
  287. expected['secrets']['vault']['qux'][-1] = \
  288. GPG_PILLAR_DECRYPTED['secrets']['vault']['qux'][-1]
  289. self.assertEqual(ret, expected)
  290. @requires_system_grains
  291. def test_decrypt_pillar_explicit_renderer(self, grains=None):
  292. '''
  293. Test recursive decryption of secrets:vault, with the renderer
  294. explicitly defined, overriding the default. Setting the default to a
  295. nonexistant renderer so we can be sure that the override happened.
  296. '''
  297. decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\
  298. decrypt_pillar_default: asdf
  299. decrypt_pillar_renderers:
  300. - asdf
  301. - gpg
  302. decrypt_pillar:
  303. - 'secrets:vault': gpg
  304. '''))
  305. opts = self._build_opts(decrypt_pillar_opts)
  306. pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
  307. ret = pillar_obj.compile_pillar()
  308. self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
  309. @requires_system_grains
  310. def test_decrypt_pillar_missing_renderer(self, grains=None):
  311. '''
  312. Test decryption using a missing renderer. It should fail, leaving the
  313. encrypted keys intact, and add an error to the pillar dictionary.
  314. '''
  315. decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\
  316. decrypt_pillar_default: asdf
  317. decrypt_pillar_renderers:
  318. - asdf
  319. decrypt_pillar:
  320. - 'secrets:vault'
  321. '''))
  322. opts = self._build_opts(decrypt_pillar_opts)
  323. pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
  324. ret = pillar_obj.compile_pillar()
  325. expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
  326. expected['_errors'] = [
  327. 'Failed to decrypt pillar key \'secrets:vault\': Decryption '
  328. 'renderer \'asdf\' is not available'
  329. ]
  330. self.assertEqual(ret, expected)
  331. @requires_system_grains
  332. def test_decrypt_pillar_invalid_renderer(self, grains=None):
  333. '''
  334. Test decryption using a renderer which is not permitted. It should
  335. fail, leaving the encrypted keys intact, and add an error to the pillar
  336. dictionary.
  337. '''
  338. decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\
  339. decrypt_pillar_default: foo
  340. decrypt_pillar_renderers:
  341. - foo
  342. - bar
  343. decrypt_pillar:
  344. - 'secrets:vault': gpg
  345. '''))
  346. opts = self._build_opts(decrypt_pillar_opts)
  347. pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
  348. ret = pillar_obj.compile_pillar()
  349. expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
  350. expected['_errors'] = [
  351. 'Failed to decrypt pillar key \'secrets:vault\': \'gpg\' is '
  352. 'not a valid decryption renderer. Valid choices are: foo, bar'
  353. ]
  354. self.assertEqual(ret, expected)