2017-01-30 07:38:48 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
|
|
|
:codeauthor: :email:`Erik Johnson <erik@saltstack.com>`
|
|
|
|
'''
|
|
|
|
|
|
|
|
# Import Python libs
|
2017-02-27 15:59:04 +00:00
|
|
|
from __future__ import absolute_import
|
2017-01-30 07:38:48 +00:00
|
|
|
import copy
|
|
|
|
import errno
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import textwrap
|
2017-04-03 16:04:09 +00:00
|
|
|
import subprocess
|
2017-01-30 07:38:48 +00:00
|
|
|
|
2017-02-27 15:59:04 +00:00
|
|
|
# Import Salt Testing libs
|
2017-04-03 16:04:09 +00:00
|
|
|
from tests.support.case import ModuleCase
|
|
|
|
from tests.support.paths import TMP, TMP_CONF_DIR
|
2017-02-27 15:59:04 +00:00
|
|
|
from tests.support.unit import skipIf
|
|
|
|
from tests.support.helpers import requires_system_grains
|
2017-01-30 07:38:48 +00:00
|
|
|
|
Python 3 Fixes (Pt. 2) (#39397)
* Typo in comment
* First convert to string if not already a string. Then to bytes under Py3.
The reason being that jids from the CLI, at least the one fed in
integration.runners.jobs.ManageTest.test_loopup_jid is loaded as an
integer, and, while the Py2 code converts JIDs to strings, under Py3, we
assume JID's are already strings.
* Mark tests which require root permissions to run
* Allow declaring that the function IS a class method.
```
Python 3.5.3 (default, Jan 21 2017, 00:29:12)
[GCC 6.3.1 20170109] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... def bar(self):
... print('bar')
...
>>> import inspect
>>> inspect.ismethod(Foo.bar)
False
>>> inspect.ismethod(Foo().bar)
True
```
On Python 2, `inspect.ismethod` returns `True` for bound and unbound
methods while on Python 3 it only returns `True` for bound methods.
The explicit `is_class_method` is to avoid instantiating the class just
to get the function signature.
* Always decode responses to the Python version native string implementation
* Just compare the objects as matching list.
Asserting same item count doesn't make that much sense.
* Py3 compatibility
* Fix saltnado tests under Py3
* Python 3 compatibility
* Show me the full traceback
* Revert "Convert fileserver data from bytes to strings"
This reverts commit e53972f8c6464c0fdeffb875c785b3ce530c7c5e.
* Revert "Under Py3, we get `bytes` when using the roots backend directly"
This reverts commit 9f73b240c1d68d785a04d898fab84837c154a5ae.
* Convert from bytes to str if not a binary file
* Py3 compatibility fixes.
Convert file contents from bytes to string if not a binary file
2017-02-14 23:20:56 +00:00
|
|
|
# Import 3rd-party libs
|
2017-04-03 16:04:09 +00:00
|
|
|
import yaml
|
Python 3 Fixes (Pt. 2) (#39397)
* Typo in comment
* First convert to string if not already a string. Then to bytes under Py3.
The reason being that jids from the CLI, at least the one fed in
integration.runners.jobs.ManageTest.test_loopup_jid is loaded as an
integer, and, while the Py2 code converts JIDs to strings, under Py3, we
assume JID's are already strings.
* Mark tests which require root permissions to run
* Allow declaring that the function IS a class method.
```
Python 3.5.3 (default, Jan 21 2017, 00:29:12)
[GCC 6.3.1 20170109] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class Foo:
... def bar(self):
... print('bar')
...
>>> import inspect
>>> inspect.ismethod(Foo.bar)
False
>>> inspect.ismethod(Foo().bar)
True
```
On Python 2, `inspect.ismethod` returns `True` for bound and unbound
methods while on Python 3 it only returns `True` for bound methods.
The explicit `is_class_method` is to avoid instantiating the class just
to get the function signature.
* Always decode responses to the Python version native string implementation
* Just compare the objects as matching list.
Asserting same item count doesn't make that much sense.
* Py3 compatibility
* Fix saltnado tests under Py3
* Python 3 compatibility
* Show me the full traceback
* Revert "Convert fileserver data from bytes to strings"
This reverts commit e53972f8c6464c0fdeffb875c785b3ce530c7c5e.
* Revert "Under Py3, we get `bytes` when using the roots backend directly"
This reverts commit 9f73b240c1d68d785a04d898fab84837c154a5ae.
* Convert from bytes to str if not a binary file
* Py3 compatibility fixes.
Convert file contents from bytes to string if not a binary file
2017-02-14 23:20:56 +00:00
|
|
|
import salt.ext.six as six
|
|
|
|
|
2017-01-30 07:38:48 +00:00
|
|
|
# Import salt libs
|
|
|
|
import salt.utils
|
2017-07-18 16:31:01 +00:00
|
|
|
import salt.utils.files
|
2017-04-03 16:04:09 +00:00
|
|
|
import salt.pillar as pillar
|
2017-01-30 07:38:48 +00:00
|
|
|
|
2017-02-27 15:59:04 +00:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2017-01-30 07:38:48 +00:00
|
|
|
|
2017-04-03 16:04:09 +00:00
|
|
|
GPG_HOMEDIR = os.path.join(TMP_CONF_DIR, 'gpgkeys')
|
|
|
|
PILLAR_BASE = os.path.join(TMP, 'test-decrypt-pillar', 'pillar')
|
2017-01-30 07:38:48 +00:00
|
|
|
TOP_SLS = os.path.join(PILLAR_BASE, 'top.sls')
|
|
|
|
GPG_SLS = os.path.join(PILLAR_BASE, 'gpg.sls')
|
|
|
|
DEFAULT_OPTS = {
|
2017-04-03 16:04:09 +00:00
|
|
|
'cachedir': os.path.join(TMP, 'rootdir', 'cache'),
|
|
|
|
'config_dir': TMP_CONF_DIR,
|
|
|
|
'extension_modules': os.path.join(TMP,
|
2017-01-30 07:38:48 +00:00
|
|
|
'test-decrypt-pillar',
|
|
|
|
'extmods'),
|
|
|
|
'pillar_roots': {'base': [PILLAR_BASE]},
|
|
|
|
'ext_pillar_first': False,
|
|
|
|
'ext_pillar': [],
|
|
|
|
'decrypt_pillar_default': 'gpg',
|
|
|
|
'decrypt_pillar_delimiter': ':',
|
|
|
|
'decrypt_pillar_renderers': ['gpg'],
|
|
|
|
}
|
|
|
|
ADDITIONAL_OPTS = (
|
2017-05-15 19:52:07 +00:00
|
|
|
'conf_file',
|
2017-01-30 07:38:48 +00:00
|
|
|
'file_roots',
|
|
|
|
'state_top',
|
|
|
|
'renderer',
|
|
|
|
'renderer_whitelist',
|
|
|
|
'renderer_blacklist',
|
|
|
|
)
|
|
|
|
|
|
|
|
TEST_KEY = '''\
|
|
|
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
|
|
|
|
|
|
|
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
|
|
|
|
=06Tz
|
|
|
|
-----END PGP PRIVATE KEY BLOCK-----
|
|
|
|
'''
|
|
|
|
|
|
|
|
GPG_PILLAR_YAML = '''\
|
|
|
|
secrets:
|
|
|
|
vault:
|
|
|
|
foo: |
|
|
|
|
-----BEGIN PGP MESSAGE-----
|
|
|
|
|
|
|
|
hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th
|
|
|
|
W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74
|
|
|
|
ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7
|
|
|
|
+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb
|
|
|
|
VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73
|
|
|
|
zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06
|
|
|
|
KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh
|
|
|
|
iFndxegN9w==
|
|
|
|
=bAuo
|
|
|
|
-----END PGP MESSAGE-----
|
|
|
|
bar: this was unencrypted already
|
|
|
|
baz: |
|
|
|
|
-----BEGIN PGP MESSAGE-----
|
|
|
|
|
|
|
|
hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz
|
|
|
|
gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf
|
|
|
|
9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7
|
|
|
|
Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2
|
|
|
|
q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V
|
|
|
|
kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl
|
|
|
|
JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY
|
|
|
|
1OZi
|
|
|
|
=7epf
|
|
|
|
-----END PGP MESSAGE-----
|
|
|
|
qux:
|
|
|
|
- foo
|
|
|
|
- bar
|
|
|
|
- |
|
|
|
|
-----BEGIN PGP MESSAGE-----
|
|
|
|
|
|
|
|
hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS
|
|
|
|
ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI
|
|
|
|
gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA
|
|
|
|
YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF
|
|
|
|
f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE
|
|
|
|
uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd
|
|
|
|
4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=
|
|
|
|
=LrCQ
|
|
|
|
-----END PGP MESSAGE-----
|
|
|
|
'''
|
|
|
|
|
|
|
|
GPG_PILLAR_ENCRYPTED = {
|
|
|
|
'secrets': {
|
|
|
|
'vault': {
|
|
|
|
'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',
|
|
|
|
'bar': 'this was unencrypted already',
|
|
|
|
'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',
|
|
|
|
'qux': [
|
|
|
|
'foo',
|
|
|
|
'bar',
|
|
|
|
'-----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'
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
GPG_PILLAR_DECRYPTED = {
|
|
|
|
'secrets': {
|
|
|
|
'vault': {
|
|
|
|
'foo': 'supersecret',
|
|
|
|
'bar': 'this was unencrypted already',
|
|
|
|
'baz': 'rosebud',
|
|
|
|
'qux': ['foo', 'bar', 'baz'],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@skipIf(not salt.utils.which('gpg'), 'GPG is not installed')
|
2017-04-03 16:04:09 +00:00
|
|
|
class DecryptGPGPillarTest(ModuleCase):
|
2017-01-30 07:38:48 +00:00
|
|
|
'''
|
|
|
|
Tests for pillar decryption
|
|
|
|
'''
|
|
|
|
maxDiff = None
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
try:
|
|
|
|
os.makedirs(GPG_HOMEDIR, mode=0o700)
|
|
|
|
except Exception:
|
|
|
|
cls.created_gpg_homedir = False
|
|
|
|
raise
|
|
|
|
else:
|
|
|
|
cls.created_gpg_homedir = True
|
|
|
|
cmd_prefix = ['gpg', '--homedir', GPG_HOMEDIR]
|
|
|
|
|
|
|
|
cmd = cmd_prefix + ['--list-keys']
|
|
|
|
log.debug('Instantiating gpg keyring using: %s', cmd)
|
2017-04-03 16:04:09 +00:00
|
|
|
output = subprocess.Popen(cmd,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
shell=False).communicate()[0]
|
2017-01-30 07:38:48 +00:00
|
|
|
log.debug('Result:\n%s', output)
|
|
|
|
|
|
|
|
cmd = cmd_prefix + ['--import', '--allow-secret-key-import']
|
|
|
|
log.debug('Importing keypair using: %s', cmd)
|
2017-04-03 16:04:09 +00:00
|
|
|
output = subprocess.Popen(cmd,
|
|
|
|
stdin=subprocess.PIPE,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
shell=False).communicate(input=six.b(TEST_KEY))[0]
|
2017-01-30 07:38:48 +00:00
|
|
|
log.debug('Result:\n%s', output)
|
|
|
|
|
|
|
|
os.makedirs(PILLAR_BASE)
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(TOP_SLS, 'w') as fp_:
|
2017-01-30 07:38:48 +00:00
|
|
|
fp_.write(textwrap.dedent('''\
|
|
|
|
base:
|
|
|
|
'*':
|
|
|
|
- gpg
|
|
|
|
'''))
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(GPG_SLS, 'w') as fp_:
|
2017-01-30 07:38:48 +00:00
|
|
|
fp_.write(GPG_PILLAR_YAML)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
2017-05-25 08:23:21 +00:00
|
|
|
cmd = ['gpg-connect-agent', '--homedir', GPG_HOMEDIR]
|
|
|
|
try:
|
|
|
|
log.debug('Killing gpg-agent using: %s', cmd)
|
|
|
|
output = subprocess.Popen(cmd,
|
|
|
|
stdin=subprocess.PIPE,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
shell=False).communicate(input=six.b('KILLAGENT'))[0]
|
|
|
|
log.debug('Result:\n%s', output)
|
|
|
|
except OSError:
|
|
|
|
log.debug('No need to kill: old gnupg doesn\'t start the agent.')
|
|
|
|
|
2017-01-30 07:38:48 +00:00
|
|
|
if cls.created_gpg_homedir:
|
|
|
|
try:
|
|
|
|
shutil.rmtree(GPG_HOMEDIR)
|
|
|
|
except OSError as exc:
|
|
|
|
# GPG socket can disappear before rmtree gets to this point
|
|
|
|
if exc.errno != errno.ENOENT:
|
|
|
|
raise
|
|
|
|
shutil.rmtree(PILLAR_BASE)
|
|
|
|
|
|
|
|
def _build_opts(self, opts):
|
|
|
|
ret = copy.deepcopy(DEFAULT_OPTS)
|
|
|
|
for item in ADDITIONAL_OPTS:
|
|
|
|
ret[item] = self.master_opts[item]
|
|
|
|
ret.update(opts)
|
|
|
|
return ret
|
|
|
|
|
|
|
|
@requires_system_grains
|
|
|
|
def test_decrypt_pillar_default_renderer(self, grains=None):
|
|
|
|
'''
|
|
|
|
Test recursive decryption of secrets:vault as well as the fallback to
|
|
|
|
default decryption renderer.
|
|
|
|
'''
|
|
|
|
decrypt_pillar_opts = yaml.safe_load(textwrap.dedent('''\
|
|
|
|
decrypt_pillar:
|
|
|
|
- 'secrets:vault'
|
|
|
|
'''))
|
|
|
|
opts = self._build_opts(decrypt_pillar_opts)
|
|
|
|
pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
|
|
|
|
ret = pillar_obj.compile_pillar()
|
|
|
|
self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
|
|
|
|
|
|
|
|
@requires_system_grains
|
|
|
|
def test_decrypt_pillar_alternate_delimiter(self, grains=None):
|
|
|
|
'''
|
|
|
|
Test recursive decryption of secrets:vault using a pipe instead of a
|
|
|
|
colon as the nesting delimiter.
|
|
|
|
'''
|
|
|
|
decrypt_pillar_opts = yaml.safe_load(textwrap.dedent('''\
|
|
|
|
decrypt_pillar_delimiter: '|'
|
|
|
|
decrypt_pillar:
|
|
|
|
- 'secrets|vault'
|
|
|
|
'''))
|
|
|
|
opts = self._build_opts(decrypt_pillar_opts)
|
|
|
|
pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
|
|
|
|
ret = pillar_obj.compile_pillar()
|
|
|
|
self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
|
|
|
|
|
|
|
|
@requires_system_grains
|
|
|
|
def test_decrypt_pillar_deeper_nesting(self, grains=None):
|
|
|
|
'''
|
|
|
|
Test recursive decryption, only with a more deeply-nested target. This
|
|
|
|
should leave the other keys in secrets:vault encrypted.
|
|
|
|
'''
|
|
|
|
decrypt_pillar_opts = yaml.safe_load(textwrap.dedent('''\
|
|
|
|
decrypt_pillar:
|
|
|
|
- 'secrets:vault:qux'
|
|
|
|
'''))
|
|
|
|
opts = self._build_opts(decrypt_pillar_opts)
|
|
|
|
pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
|
|
|
|
ret = pillar_obj.compile_pillar()
|
|
|
|
expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
|
|
|
|
expected['secrets']['vault']['qux'][-1] = \
|
|
|
|
GPG_PILLAR_DECRYPTED['secrets']['vault']['qux'][-1]
|
|
|
|
self.assertEqual(ret, expected)
|
|
|
|
|
|
|
|
@requires_system_grains
|
|
|
|
def test_decrypt_pillar_explicit_renderer(self, grains=None):
|
|
|
|
'''
|
|
|
|
Test recursive decryption of secrets:vault, with the renderer
|
|
|
|
explicitly defined, overriding the default. Setting the default to a
|
|
|
|
nonexistant renderer so we can be sure that the override happened.
|
|
|
|
'''
|
|
|
|
decrypt_pillar_opts = yaml.safe_load(textwrap.dedent('''\
|
|
|
|
decrypt_pillar_default: asdf
|
|
|
|
decrypt_pillar_renderers:
|
|
|
|
- asdf
|
|
|
|
- gpg
|
|
|
|
decrypt_pillar:
|
|
|
|
- 'secrets:vault': gpg
|
|
|
|
'''))
|
|
|
|
opts = self._build_opts(decrypt_pillar_opts)
|
|
|
|
pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
|
|
|
|
ret = pillar_obj.compile_pillar()
|
|
|
|
self.assertEqual(ret, GPG_PILLAR_DECRYPTED)
|
|
|
|
|
|
|
|
@requires_system_grains
|
|
|
|
def test_decrypt_pillar_missing_renderer(self, grains=None):
|
|
|
|
'''
|
|
|
|
Test decryption using a missing renderer. It should fail, leaving the
|
|
|
|
encrypted keys intact, and add an error to the pillar dictionary.
|
|
|
|
'''
|
|
|
|
decrypt_pillar_opts = yaml.safe_load(textwrap.dedent('''\
|
|
|
|
decrypt_pillar_default: asdf
|
|
|
|
decrypt_pillar_renderers:
|
|
|
|
- asdf
|
|
|
|
decrypt_pillar:
|
|
|
|
- 'secrets:vault'
|
|
|
|
'''))
|
|
|
|
opts = self._build_opts(decrypt_pillar_opts)
|
|
|
|
pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
|
|
|
|
ret = pillar_obj.compile_pillar()
|
|
|
|
expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
|
|
|
|
expected['_errors'] = [
|
|
|
|
'Failed to decrypt pillar key \'secrets:vault\': Decryption '
|
|
|
|
'renderer \'asdf\' is not available'
|
|
|
|
]
|
|
|
|
self.assertEqual(ret, expected)
|
|
|
|
|
|
|
|
@requires_system_grains
|
|
|
|
def test_decrypt_pillar_invalid_renderer(self, grains=None):
|
|
|
|
'''
|
|
|
|
Test decryption using a renderer which is not permitted. It should
|
|
|
|
fail, leaving the encrypted keys intact, and add an error to the pillar
|
|
|
|
dictionary.
|
|
|
|
'''
|
|
|
|
decrypt_pillar_opts = yaml.safe_load(textwrap.dedent('''\
|
|
|
|
decrypt_pillar_default: foo
|
|
|
|
decrypt_pillar_renderers:
|
|
|
|
- foo
|
|
|
|
- bar
|
|
|
|
decrypt_pillar:
|
|
|
|
- 'secrets:vault': gpg
|
|
|
|
'''))
|
|
|
|
opts = self._build_opts(decrypt_pillar_opts)
|
|
|
|
pillar_obj = pillar.Pillar(opts, grains, 'test', 'base')
|
|
|
|
ret = pillar_obj.compile_pillar()
|
|
|
|
expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED)
|
|
|
|
expected['_errors'] = [
|
|
|
|
'Failed to decrypt pillar key \'secrets:vault\': \'gpg\' is '
|
|
|
|
'not a valid decryption renderer. Valid choices are: foo, bar'
|
|
|
|
]
|
|
|
|
self.assertEqual(ret, expected)
|