test_pillar.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. # -*- coding: utf-8 -*-
  2. """
  3. :codeauthor: Erik Johnson <erik@saltstack.com>
  4. """
  5. from __future__ import absolute_import
  6. import copy
  7. import errno
  8. import logging
  9. import os
  10. import shutil
  11. import subprocess
  12. import textwrap
  13. import pytest
  14. import salt.pillar as pillar
  15. import salt.utils.files
  16. import salt.utils.path
  17. import salt.utils.stringutils
  18. import salt.utils.yaml
  19. from tests.support.case import ModuleCase
  20. from tests.support.helpers import dedent, requires_system_grains, slowTest
  21. from tests.support.runtests import RUNTIME_VARS
  22. from tests.support.unit import skipIf
  23. log = logging.getLogger(__name__)
  24. TEST_KEY = """\
  25. -----BEGIN PGP PRIVATE KEY BLOCK-----
  26. lQOYBFiKrcYBCADAj92+fz20uKxxH0ffMwcryGG9IogkiUi2QrNYilB4hwrY5Qt7
  27. Sbywlk/mSDMcABxMxS0vegqc5pgglvAnsi9w7j//9nfjiirsyiTYOOD1akTFQr7b
  28. qT6zuGFA4oYmYHvfBOena485qvlyitYLKYT9h27TDiiH6Jgt4xSRbjeyhTf3/fKD
  29. JzHA9ii5oeVi1pH/8/4USgXanBdKwO0JKQtci+PF0qe/nkzRswqTIkdgx1oyNUqL
  30. tYJ0XPOy+UyOC4J4QDIt9PQbAmiur8By4g2lLYWlGOCjs7Fcj3n5meWKzf1pmXoY
  31. lAnSab8kUZSSkoWQoTO7RbjFypULKCZui45/ABEBAAEAB/wM1wsAMtfYfx/wgxd1
  32. yJ9HyhrKU80kMotIq/Xth3uKLecJQ2yakfYlCEDXqCTQTymT7OnwaoDeqXmnYqks
  33. 3HLRYvGdjb+8ym/GTkxapqBJfQaM6MB1QTnPHhJOE0zCrlhULK2NulxYihAMFTnk
  34. kKYviaJYLG+DcH0FQkkS0XihTKcqnsoJiS6iNd5SME3pa0qijR0D5f78fkvNzzEE
  35. 9vgAX1TgQ5PDJGN6nYlW2bWxTcg+FR2cUAQPTiP9wXCH6VyJoQay7KHVr3r/7SsU
  36. 89otfcx5HVDYPrez6xnP6wN0P/mKxCDbkERLDjZjWOmNXg2zn+/t3u02e+ybfAIp
  37. kTTxBADY/FmPgLpJ2bpcPH141twpHwhKIbENlTB9745Qknr6aLA0QVCkz49/3joO
  38. Sj+SZ7Jhl6cfbynrfHwX3b1bOFTzBUH2Tsi0HX40PezEFH0apf55FLZuMOBt/lc1
  39. ET6evpIHF0dcM+BvZa7E7MyTyEq8S7Cc9RoJyfeGbS7MG5FfuwQA4y9QOb/OQglq
  40. ZffkVItwY52RKWb/b2WQmt+IcVax/j7DmBva765SIfPDvOCMrYhJBI/uYHQ0Zia7
  41. SnC9+ez55wdYqgHkYojc21CIOnUvsPSj+rOpryoXzmcTuvKeVIyIA0h/mQyWjimR
  42. ENrikC4+O8GBMY6V4uvS4EFhLfHE9g0D/20lNOKkpAKPenr8iAPWcl0/pijJCGxF
  43. agnT7O2GQ9Lr5hSjW86agkevbGktu2ja5t/fHq0wpLQ4DVLMrR0/poaprTr307kW
  44. AlQV3z/C2cMHNysz4ulOgQrudQbhUEz2A8nQxRtIfWunkEugKLr1QiCkE1LJW8Np
  45. ZLxE6Qp0/KzdQva0HVNhbHQgR1BHIDxlcmlrQHNhbHRzdGFjay5jb20+iQFUBBMB
  46. CAA+FiEE+AxQ1ELHGEyFTZPYw5x3k9EbHGsFAliKrcYCGwMFCQPCZwAFCwkIBwIG
  47. FQgJCgsCBBYCAwECHgECF4AACgkQw5x3k9EbHGubUAf+PLdp1oTLVokockZgLyIQ
  48. wxOd3ofNOgNk4QoAkSMNSbtnYoQFKumRw/yGyPSIoHMsOC/ga98r8TAJEKfx3DLA
  49. rsD34oMAaYUT+XUd0KoSmlHqBrtDD1+eBASKYsCosHpCiKuQFfLKSxvpEr2YyL8L
  50. X3Q2TY5zFlGA9Eeq5g+rlb++yRZrruFN28EWtY/pyXFZgIB30ReDwPkM9hrioPZM
  51. 0Qf3+dWZSK1rWViclB51oNy4un9stTiFZptAqz4NTNssU5A4AcNQPwBwnKIYoE58
  52. Y/Zyv8HzILGykT+qFebqRlRBI/13eHdzgJOL1iPRfjTk5Cvr+vcyIxAklXOP81ja
  53. B50DmARYiq3GAQgArnzu4SPCCQGNcCNxN4QlMP5TNvRsm5KrPbcO9j8HPfB+DRXs
  54. 6B3mnuR6OJg7YuC0C2A/m2dSHJKkF0f2AwFRpxLjJ2iAFbrZAW/N0vZDx8zO+YAU
  55. HyLu0V04wdCE5DTLkgfWNR+0uMa8qZ4Kn56Gv7O+OFE7zgTHeZ7psWlxdafeW7u6
  56. zlC/3DWksNtuNb0vQDNMM4vgXbnORIfXdyh41zvEEnr/rKw8DuJAmo20mcv6Qi51
  57. PqqyM62ddQOEVfiMs9l4vmwZAjGFNFNInyPXnogL6UPCDmizb6hh8aX/MwG/XFIG
  58. KMJWbAVGpyBuqljKIt3qLu/s8ouPqkEN+f+nGwARAQABAAf+NA36d/kieGxZpTQ1
  59. oQHP1Jty+OiXhBwP8SPtF0J7ZxuZh07cs+zDsfBok/y6bsepfuFSaIq84OBQis+B
  60. kajxkp3cXZPb7l+lQLv5k++7Dd7Ien+ewSE7TQN6HLwYATrM5n5nBcc1M5C6lQGc
  61. mr0A5yz42TVG2bHsTpi9kBtsaVRSPUHSh8A8T6eOyCrT+/CAJVEEf7JyNyaqH1dy
  62. LuxI1VF3ySDEtFzuwN8EZQP9Yz/4AVyEQEA7WkNEwSQsBi2bWgWEdG+qjqnL+YKa
  63. vwe7/aJYPeL1zICnP/Osd/UcpDxR78MbozstbRljML0fTLj7UJ+XDazwv+Kl0193
  64. 2ZK2QQQAwgXvS19MYNkHO7kbNVLt1VE2ll901iC9GFHBpFUam6gmoHXpCarB+ShH
  65. 8x25aoUu4MxHmFxXd+Zq3d6q2yb57doWoPgvqcefpGmigaITnb1jhV2rt65V8deA
  66. SQazZNqBEBbZNIhfn6ObxHXXvaYaqq/UOEQ7uKyR9WMJT/rmqMEEAOY5h1R1t7AB
  67. JZ5VnhyAhdsNWw1gTcXB3o8gKz4vjdnPm0F4aVIPfB3BukETDc3sc2tKmCfUF7I7
  68. oOrh7iRez5F0RIC3KDzXF8qUuWBfPViww45JgftdKsecCIlEEYCoc+3goX0su2bP
  69. V1MDuHijMGTJCBABDgizNb0oynW5xcrbA/0QnKfpTwi7G3oRcJWv2YebVDRcU+SP
  70. dOYhq6SnmWPizEIljRG/X7FHJB+W7tzryO3sCDTAYwxFrfMwvJ2PwnAYI4349zYd
  71. lC28HowUkBYNhwBXc48xCfyhPZtD0aLx/OX1oLZ/vi8gd8TusgGupV/JjkFVO+Nd
  72. +shN/UEAldwqkkY2iQE8BBgBCAAmFiEE+AxQ1ELHGEyFTZPYw5x3k9EbHGsFAliK
  73. rcYCGwwFCQPCZwAACgkQw5x3k9EbHGu4wwf/dRFat91BRX1TJfwJl5otoAXpItYM
  74. 6kdWWf1Eb1BicAvXhI078MSH4WXdKkJjJr1fFP8Ynil513H4Mzb0rotMAhb0jLSA
  75. lSRkMbhMvPxoS2kaYzioaBpp8yXpGiNo7dF+PJXSm/Uwp3AkcFjoVbBOqDWGgxMi
  76. DvDAstzLZ9dIcmr+OmcRQykKOKXlhEl3HnR5CyuPrA8hdVup4oeVwdkJhfJFKLLb
  77. 3fR26wxJOmIOAt24eAUy721WfQ9txNAmhdy8mY842ODZESw6WatrQjRfuqosDgrk
  78. jc0cCHsEqJNZ2AB+1uEl3tcH0tyAFJa33F0znSonP17SS1Ff9sgHYBVLUg==
  79. =06Tz
  80. -----END PGP PRIVATE KEY BLOCK-----
  81. """
  82. GPG_PILLAR_YAML = """\
  83. secrets:
  84. vault:
  85. foo: |
  86. -----BEGIN PGP MESSAGE-----
  87. hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th
  88. W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74
  89. ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7
  90. +KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb
  91. VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73
  92. zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06
  93. KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh
  94. iFndxegN9w==
  95. =bAuo
  96. -----END PGP MESSAGE-----
  97. bar: this was unencrypted already
  98. baz: |
  99. -----BEGIN PGP MESSAGE-----
  100. hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz
  101. gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf
  102. 9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7
  103. Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2
  104. q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V
  105. kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl
  106. JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY
  107. 1OZi
  108. =7epf
  109. -----END PGP MESSAGE-----
  110. qux:
  111. - foo
  112. - bar
  113. - |
  114. -----BEGIN PGP MESSAGE-----
  115. hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS
  116. ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI
  117. gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA
  118. YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF
  119. f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE
  120. uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd
  121. 4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=
  122. =LrCQ
  123. -----END PGP MESSAGE-----
  124. """
  125. GPG_PILLAR_ENCRYPTED = {
  126. "secrets": {
  127. "vault": {
  128. "foo": "-----BEGIN PGP MESSAGE-----\n"
  129. "\n"
  130. "hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th\n"
  131. "W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74\n"
  132. "ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7\n"
  133. "+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb\n"
  134. "VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73\n"
  135. "zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06\n"
  136. "KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh\n"
  137. "iFndxegN9w==\n"
  138. "=bAuo\n"
  139. "-----END PGP MESSAGE-----\n",
  140. "bar": "this was unencrypted already",
  141. "baz": "-----BEGIN PGP MESSAGE-----\n"
  142. "\n"
  143. "hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz\n"
  144. "gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf\n"
  145. "9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7\n"
  146. "Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2\n"
  147. "q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V\n"
  148. "kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl\n"
  149. "JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY\n"
  150. "1OZi\n"
  151. "=7epf\n"
  152. "-----END PGP MESSAGE-----\n",
  153. "qux": [
  154. "foo",
  155. "bar",
  156. "-----BEGIN PGP MESSAGE-----\n"
  157. "\n"
  158. "hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS\n"
  159. "ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI\n"
  160. "gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA\n"
  161. "YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF\n"
  162. "f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE\n"
  163. "uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd\n"
  164. "4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=\n"
  165. "=LrCQ\n"
  166. "-----END PGP MESSAGE-----\n",
  167. ],
  168. },
  169. },
  170. }
  171. GPG_PILLAR_DECRYPTED = {
  172. "secrets": {
  173. "vault": {
  174. "foo": "supersecret",
  175. "bar": "this was unencrypted already",
  176. "baz": "rosebud",
  177. "qux": ["foo", "bar", "baz"],
  178. },
  179. },
  180. }
  181. class _CommonBase(ModuleCase):
  182. @classmethod
  183. def setUpClass(cls):
  184. cls.pillar_base = os.path.join(
  185. RUNTIME_VARS.TMP, "test-decrypt-pillar", "pillar"
  186. )
  187. cls.top_sls = os.path.join(cls.pillar_base, "top.sls")
  188. cls.gpg_sls = os.path.join(cls.pillar_base, "gpg.sls")
  189. cls.default_opts = {
  190. "cachedir": os.path.join(RUNTIME_VARS.TMP, "rootdir", "cache"),
  191. "optimization_order": [0, 1, 2],
  192. "extension_modules": os.path.join(
  193. RUNTIME_VARS.TMP, "test-decrypt-pillar", "extmods"
  194. ),
  195. "pillar_roots": {"base": [cls.pillar_base]},
  196. "ext_pillar_first": False,
  197. "ext_pillar": [],
  198. "decrypt_pillar_default": "gpg",
  199. "decrypt_pillar_delimiter": ":",
  200. "decrypt_pillar_renderers": ["gpg"],
  201. }
  202. cls.additional_opts = (
  203. "conf_file",
  204. "file_roots",
  205. "state_top",
  206. "renderer",
  207. "renderer_whitelist",
  208. "renderer_blacklist",
  209. )
  210. cls.gpg_homedir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "gpgkeys")
  211. def _build_opts(self, opts):
  212. ret = copy.deepcopy(self.default_opts)
  213. for item in self.additional_opts:
  214. ret[item] = self.master_opts[item]
  215. ret.update(opts)
  216. return ret
  217. @pytest.mark.windows_whitelisted
  218. class BasePillarTest(_CommonBase):
  219. """
  220. Tests for pillar decryption
  221. """
  222. @classmethod
  223. def setUpClass(cls):
  224. super(BasePillarTest, cls).setUpClass()
  225. os.makedirs(cls.pillar_base)
  226. with salt.utils.files.fopen(cls.top_sls, "w") as fp_:
  227. fp_.write(
  228. textwrap.dedent(
  229. """\
  230. base:
  231. 'N@mins not L@minion':
  232. - ng1
  233. 'N@missing_minion':
  234. - ng2
  235. """
  236. )
  237. )
  238. with salt.utils.files.fopen(
  239. os.path.join(cls.pillar_base, "ng1.sls"), "w"
  240. ) as fp_:
  241. fp_.write("pillar_from_nodegroup: True")
  242. with salt.utils.files.fopen(
  243. os.path.join(cls.pillar_base, "ng2.sls"), "w"
  244. ) as fp_:
  245. fp_.write("pillar_from_nodegroup_with_ghost: True")
  246. @classmethod
  247. def tearDownClass(cls):
  248. shutil.rmtree(cls.pillar_base)
  249. def test_pillar_top_compound_match(self, grains=None):
  250. """
  251. Test that a compound match topfile that refers to a nodegroup via N@ works
  252. as expected.
  253. """
  254. if not grains:
  255. grains = {}
  256. grains["os"] = "Fedora"
  257. nodegroup_opts = salt.utils.yaml.safe_load(
  258. textwrap.dedent(
  259. """\
  260. nodegroups:
  261. min: minion
  262. sub_min: sub_minion
  263. mins: N@min or N@sub_min
  264. missing_minion: L@minion,ghostminion
  265. """
  266. )
  267. )
  268. opts = self._build_opts(nodegroup_opts)
  269. pillar_obj = pillar.Pillar(opts, grains, "minion", "base")
  270. ret = pillar_obj.compile_pillar()
  271. self.assertEqual(ret.get("pillar_from_nodegroup_with_ghost"), True)
  272. self.assertEqual(ret.get("pillar_from_nodegroup"), None)
  273. sub_pillar_obj = pillar.Pillar(opts, grains, "sub_minion", "base")
  274. sub_ret = sub_pillar_obj.compile_pillar()
  275. self.assertEqual(sub_ret.get("pillar_from_nodegroup_with_ghost"), None)
  276. self.assertEqual(sub_ret.get("pillar_from_nodegroup"), True)
  277. @skipIf(not salt.utils.path.which("gpg"), "GPG is not installed")
  278. @pytest.mark.windows_whitelisted
  279. class DecryptGPGPillarTest(_CommonBase):
  280. """
  281. Tests for pillar decryption
  282. """
  283. maxDiff = None
  284. @classmethod
  285. def setUpClass(cls):
  286. super(DecryptGPGPillarTest, cls).setUpClass()
  287. try:
  288. os.makedirs(cls.gpg_homedir, mode=0o700)
  289. except Exception: # pylint: disable=broad-except
  290. cls.created_gpg_homedir = False
  291. raise
  292. else:
  293. cls.created_gpg_homedir = True
  294. cmd_prefix = ["gpg", "--homedir", cls.gpg_homedir]
  295. cmd = cmd_prefix + ["--list-keys"]
  296. log.debug("Instantiating gpg keyring using: %s", cmd)
  297. output = subprocess.Popen(
  298. cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False
  299. ).communicate()[0]
  300. log.debug("Result:\n%s", output)
  301. cmd = cmd_prefix + ["--import", "--allow-secret-key-import"]
  302. log.debug("Importing keypair using: %s", cmd)
  303. output = subprocess.Popen(
  304. cmd,
  305. stdin=subprocess.PIPE,
  306. stdout=subprocess.PIPE,
  307. stderr=subprocess.STDOUT,
  308. shell=False,
  309. ).communicate(input=salt.utils.stringutils.to_bytes(TEST_KEY))[0]
  310. log.debug("Result:\n%s", output)
  311. os.makedirs(cls.pillar_base)
  312. with salt.utils.files.fopen(cls.top_sls, "w") as fp_:
  313. fp_.write(
  314. textwrap.dedent(
  315. """\
  316. base:
  317. '*':
  318. - gpg
  319. """
  320. )
  321. )
  322. with salt.utils.files.fopen(cls.gpg_sls, "w") as fp_:
  323. fp_.write(GPG_PILLAR_YAML)
  324. @classmethod
  325. def tearDownClass(cls):
  326. cmd = ["gpg-connect-agent", "--homedir", cls.gpg_homedir]
  327. try:
  328. log.debug("Killing gpg-agent using: %s", cmd)
  329. output = subprocess.Popen(
  330. cmd,
  331. stdin=subprocess.PIPE,
  332. stdout=subprocess.PIPE,
  333. stderr=subprocess.STDOUT,
  334. shell=False,
  335. ).communicate(input=b"KILLAGENT")[0]
  336. log.debug("Result:\n%s", output)
  337. except OSError:
  338. log.debug("No need to kill: old gnupg doesn't start the agent.")
  339. if cls.created_gpg_homedir:
  340. try:
  341. shutil.rmtree(cls.gpg_homedir)
  342. except OSError as exc:
  343. # GPG socket can disappear before rmtree gets to this point
  344. if exc.errno != errno.ENOENT:
  345. raise
  346. shutil.rmtree(cls.pillar_base)
  347. @requires_system_grains
  348. def test_decrypt_pillar_default_renderer(self, grains=None):
  349. """
  350. Test recursive decryption of secrets:vault as well as the fallback to
  351. default decryption renderer.
  352. """
  353. decrypt_pillar_opts = salt.utils.yaml.safe_load(
  354. textwrap.dedent(
  355. """\
  356. decrypt_pillar:
  357. - 'secrets:vault'
  358. """
  359. )
  360. )
  361. opts = self._build_opts(decrypt_pillar_opts)
  362. pillar_obj = pillar.Pillar(opts, grains, "test", "base")
  363. ret = pillar_obj.compile_pillar()
  364. self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
  365. @requires_system_grains
  366. @slowTest
  367. def test_decrypt_pillar_alternate_delimiter(self, grains=None):
  368. """
  369. Test recursive decryption of secrets:vault using a pipe instead of a
  370. colon as the nesting delimiter.
  371. """
  372. decrypt_pillar_opts = salt.utils.yaml.safe_load(
  373. textwrap.dedent(
  374. """\
  375. decrypt_pillar_delimiter: '|'
  376. decrypt_pillar:
  377. - 'secrets|vault'
  378. """
  379. )
  380. )
  381. opts = self._build_opts(decrypt_pillar_opts)
  382. pillar_obj = pillar.Pillar(opts, grains, "test", "base")
  383. ret = pillar_obj.compile_pillar()
  384. self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
  385. @requires_system_grains
  386. def test_decrypt_pillar_deeper_nesting(self, grains=None):
  387. """
  388. Test recursive decryption, only with a more deeply-nested target. This
  389. should leave the other keys in secrets:vault encrypted.
  390. """
  391. decrypt_pillar_opts = salt.utils.yaml.safe_load(
  392. textwrap.dedent(
  393. """\
  394. decrypt_pillar:
  395. - 'secrets:vault:qux'
  396. """
  397. )
  398. )
  399. opts = self._build_opts(decrypt_pillar_opts)
  400. pillar_obj = pillar.Pillar(opts, grains, "test", "base")
  401. ret = pillar_obj.compile_pillar()
  402. expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
  403. expected["secrets"]["vault"]["qux"][-1] = GPG_PILLAR_DECRYPTED["secrets"][
  404. "vault"
  405. ]["qux"][-1]
  406. self.assertEqual(ret, expected)
  407. @requires_system_grains
  408. def test_decrypt_pillar_explicit_renderer(self, grains=None):
  409. """
  410. Test recursive decryption of secrets:vault, with the renderer
  411. explicitly defined, overriding the default. Setting the default to a
  412. nonexistent renderer so we can be sure that the override happened.
  413. """
  414. decrypt_pillar_opts = salt.utils.yaml.safe_load(
  415. textwrap.dedent(
  416. """\
  417. decrypt_pillar_default: asdf
  418. decrypt_pillar_renderers:
  419. - asdf
  420. - gpg
  421. decrypt_pillar:
  422. - 'secrets:vault': gpg
  423. """
  424. )
  425. )
  426. opts = self._build_opts(decrypt_pillar_opts)
  427. pillar_obj = pillar.Pillar(opts, grains, "test", "base")
  428. ret = pillar_obj.compile_pillar()
  429. self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
  430. @requires_system_grains
  431. def test_decrypt_pillar_missing_renderer(self, grains=None):
  432. """
  433. Test decryption using a missing renderer. It should fail, leaving the
  434. encrypted keys intact, and add an error to the pillar dictionary.
  435. """
  436. decrypt_pillar_opts = salt.utils.yaml.safe_load(
  437. textwrap.dedent(
  438. """\
  439. decrypt_pillar_default: asdf
  440. decrypt_pillar_renderers:
  441. - asdf
  442. decrypt_pillar:
  443. - 'secrets:vault'
  444. """
  445. )
  446. )
  447. opts = self._build_opts(decrypt_pillar_opts)
  448. pillar_obj = pillar.Pillar(opts, grains, "test", "base")
  449. ret = pillar_obj.compile_pillar()
  450. expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
  451. expected["_errors"] = [
  452. "Failed to decrypt pillar key 'secrets:vault': Decryption "
  453. "renderer 'asdf' is not available"
  454. ]
  455. self.assertEqual(ret["_errors"], expected["_errors"])
  456. self.assertEqual(
  457. ret["secrets"]["vault"]["foo"], expected["secrets"]["vault"]["foo"]
  458. )
  459. self.assertEqual(
  460. ret["secrets"]["vault"]["bar"], expected["secrets"]["vault"]["bar"]
  461. )
  462. self.assertEqual(
  463. ret["secrets"]["vault"]["baz"], expected["secrets"]["vault"]["baz"]
  464. )
  465. self.assertEqual(
  466. ret["secrets"]["vault"]["qux"], expected["secrets"]["vault"]["qux"]
  467. )
  468. @requires_system_grains
  469. def test_decrypt_pillar_invalid_renderer(self, grains=None):
  470. """
  471. Test decryption using a renderer which is not permitted. It should
  472. fail, leaving the encrypted keys intact, and add an error to the pillar
  473. dictionary.
  474. """
  475. decrypt_pillar_opts = salt.utils.yaml.safe_load(
  476. textwrap.dedent(
  477. """\
  478. decrypt_pillar_default: foo
  479. decrypt_pillar_renderers:
  480. - foo
  481. - bar
  482. decrypt_pillar:
  483. - 'secrets:vault': gpg
  484. """
  485. )
  486. )
  487. opts = self._build_opts(decrypt_pillar_opts)
  488. pillar_obj = pillar.Pillar(opts, grains, "test", "base")
  489. ret = pillar_obj.compile_pillar()
  490. expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
  491. expected["_errors"] = [
  492. "Failed to decrypt pillar key 'secrets:vault': 'gpg' is "
  493. "not a valid decryption renderer. Valid choices are: foo, bar"
  494. ]
  495. self.assertEqual(ret["_errors"], expected["_errors"])
  496. self.assertEqual(
  497. ret["secrets"]["vault"]["foo"], expected["secrets"]["vault"]["foo"]
  498. )
  499. self.assertEqual(
  500. ret["secrets"]["vault"]["bar"], expected["secrets"]["vault"]["bar"]
  501. )
  502. self.assertEqual(
  503. ret["secrets"]["vault"]["baz"], expected["secrets"]["vault"]["baz"]
  504. )
  505. self.assertEqual(
  506. ret["secrets"]["vault"]["qux"], expected["secrets"]["vault"]["qux"]
  507. )
  508. @pytest.mark.windows_whitelisted
  509. class RefreshPillarTest(ModuleCase):
  510. """
  511. These tests validate the behavior defined in the documentation:
  512. https://docs.saltstack.com/en/latest/topics/pillar/#in-memory-pillar-data-vs-on-demand-pillar-data
  513. These tests also serve as a regression test for:
  514. https://github.com/saltstack/salt/issues/54941
  515. """
  516. def cleanup_pillars(self, top_path, pillar_path):
  517. os.remove(top_path)
  518. os.remove(pillar_path)
  519. self.run_function("saltutil.refresh_pillar", arg=(True,))
  520. def create_pillar(self, key):
  521. """
  522. Utility method to create a pillar for the minion and a value of true,
  523. this method also removes and cleans up the pillar at the end of the
  524. test.
  525. """
  526. top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls")
  527. pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "test_pillar.sls")
  528. with salt.utils.files.fopen(top_path, "w") as fd:
  529. fd.write(
  530. dedent(
  531. """
  532. base:
  533. 'minion':
  534. - test_pillar
  535. """
  536. )
  537. )
  538. with salt.utils.files.fopen(pillar_path, "w") as fd:
  539. fd.write(
  540. dedent(
  541. """
  542. {}: true
  543. """.format(
  544. key
  545. )
  546. )
  547. )
  548. self.addCleanup(self.cleanup_pillars, top_path, pillar_path)
  549. @slowTest
  550. def test_pillar_refresh_pillar_raw(self):
  551. """
  552. Validate the minion's pillar.raw call behavior for new pillars
  553. """
  554. key = "issue-54941-raw"
  555. # We do not expect to see the pillar beacuse it does not exist yet
  556. val = self.run_function("pillar.raw", arg=(key,))
  557. assert val == {}
  558. self.create_pillar(key)
  559. # The pillar exists now but raw reads it from in-memory pillars
  560. val = self.run_function("pillar.raw", arg=(key,))
  561. assert val == {}
  562. # Calling refresh_pillar to update in-memory pillars
  563. ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
  564. # The pillar can now be read from in-memory pillars
  565. val = self.run_function("pillar.raw", arg=(key,))
  566. assert val is True, repr(val)
  567. @slowTest
  568. def test_pillar_refresh_pillar_get(self):
  569. """
  570. Validate the minion's pillar.get call behavior for new pillars
  571. """
  572. key = "issue-54941-get"
  573. # We do not expect to see the pillar beacuse it does not exist yet
  574. val = self.run_function("pillar.get", arg=(key,))
  575. assert val == ""
  576. top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls")
  577. pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "test_pillar.sls")
  578. self.create_pillar(key)
  579. # The pillar exists now but get reads it from in-memory pillars, no
  580. # refresh happens
  581. val = self.run_function("pillar.get", arg=(key,))
  582. assert val == ""
  583. # Calling refresh_pillar to update in-memory pillars
  584. ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
  585. assert ret is True
  586. # The pillar can now be read from in-memory pillars
  587. val = self.run_function("pillar.get", arg=(key,))
  588. assert val is True, repr(val)
  589. @slowTest
  590. def test_pillar_refresh_pillar_item(self):
  591. """
  592. Validate the minion's pillar.item call behavior for new pillars
  593. """
  594. key = "issue-54941-item"
  595. # We do not expect to see the pillar beacuse it does not exist yet
  596. val = self.run_function("pillar.item", arg=(key,))
  597. assert key in val
  598. assert val[key] == ""
  599. self.create_pillar(key)
  600. # The pillar exists now but get reads it from in-memory pillars, no
  601. # refresh happens
  602. val = self.run_function("pillar.item", arg=(key,))
  603. assert key in val
  604. assert val[key] == ""
  605. # Calling refresh_pillar to update in-memory pillars
  606. ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
  607. assert ret is True
  608. # The pillar can now be read from in-memory pillars
  609. val = self.run_function("pillar.item", arg=(key,))
  610. assert key in val
  611. assert val[key] is True
  612. @slowTest
  613. def test_pillar_refresh_pillar_items(self):
  614. """
  615. Validate the minion's pillar.item call behavior for new pillars
  616. """
  617. key = "issue-54941-items"
  618. # We do not expect to see the pillar beacuse it does not exist yet
  619. val = self.run_function("pillar.items")
  620. assert key not in val
  621. self.create_pillar(key)
  622. # A pillar.items call sees the pillar right away because a
  623. # refresh_pillar event is fired.
  624. val = self.run_function("pillar.items")
  625. assert key in val
  626. assert val[key] is True
  627. @slowTest
  628. def test_pillar_refresh_pillar_ping(self):
  629. """
  630. Validate the minion's test.ping does not update pillars
  631. See: https://github.com/saltstack/salt/issues/54941
  632. """
  633. key = "issue-54941-ping"
  634. # We do not expect to see the pillar beacuse it does not exist yet
  635. val = self.run_function("pillar.item", arg=(key,))
  636. assert key in val
  637. assert val[key] == ""
  638. self.create_pillar(key)
  639. val = self.run_function("test.ping")
  640. assert val is True
  641. # The pillar exists now but get reads it from in-memory pillars, no
  642. # refresh happens
  643. val = self.run_function("pillar.item", arg=(key,))
  644. assert key in val
  645. assert val[key] == ""
  646. # Calling refresh_pillar to update in-memory pillars
  647. ret = self.run_function("saltutil.refresh_pillar", arg=(True,))
  648. assert ret is True
  649. # The pillar can now be read from in-memory pillars
  650. val = self.run_function("pillar.item", arg=(key,))
  651. assert key in val
  652. assert val[key] is True