mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Merge branch 'develop' into panos/develop
This commit is contained in:
commit
2b516313f2
@ -1,6 +1,6 @@
|
||||
=======================
|
||||
salt.modules.kubernetes
|
||||
=======================
|
||||
======================
|
||||
salt.states.kubernetes
|
||||
======================
|
||||
|
||||
.. automodule:: salt.modules.kubernetes
|
||||
.. automodule:: salt.states.kubernetes
|
||||
:members:
|
||||
|
@ -49,7 +49,7 @@ Set up an initial profile at ``/etc/salt/cloud.profiles`` or in the
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
joyent_512
|
||||
joyent_512:
|
||||
provider: my-joyent-config
|
||||
size: g4-highcpu-512M
|
||||
image: ubuntu-16.04
|
||||
|
@ -63,10 +63,10 @@ environments (i.e. ``saltenvs``) have been added:
|
||||
available as saltenvs.
|
||||
|
||||
Salt Cloud Features
|
||||
===================
|
||||
-------------------
|
||||
|
||||
Pre-Flight Commands
|
||||
-------------------
|
||||
===================
|
||||
|
||||
Support has been added for specified "preflight commands" to run on a VM before
|
||||
the deploy script is run. These must be defined as a list in a cloud configuration
|
||||
@ -99,22 +99,22 @@ populated as a list of domain roles.
|
||||
|
||||
|
||||
Deprecations
|
||||
============
|
||||
------------
|
||||
|
||||
Configuration Option Deprecations
|
||||
---------------------------------
|
||||
=================================
|
||||
|
||||
- The ``requests_lib`` configuration option has been removed. Please use
|
||||
``backend`` instead.
|
||||
|
||||
Profitbricks Cloud Updated Dependency
|
||||
-------------------------------------
|
||||
=====================================
|
||||
|
||||
The minimum version of the `profitbrick` python package for the `profitbricks`
|
||||
The minimum version of the ``profitbrick`` python package for the ``profitbricks``
|
||||
cloud driver has changed from 3.0.0 to 3.1.0.
|
||||
|
||||
Module Deprecations
|
||||
-------------------
|
||||
===================
|
||||
|
||||
The ``blockdev`` execution module has been removed. Its functions were merged
|
||||
with the ``disk`` module. Please use the ``disk`` execution module instead.
|
||||
@ -154,7 +154,7 @@ The ``win_service`` module had the following changes:
|
||||
``service_type`` instead.
|
||||
|
||||
Runner Deprecations
|
||||
-------------------
|
||||
===================
|
||||
|
||||
The ``manage`` runner had the following changes:
|
||||
|
||||
@ -162,7 +162,7 @@ The ``manage`` runner had the following changes:
|
||||
use ``salt-ssh`` roster entries for the host instead.
|
||||
|
||||
State Deprecations
|
||||
------------------
|
||||
==================
|
||||
|
||||
The ``archive`` state had the following changes:
|
||||
|
||||
@ -185,7 +185,7 @@ The ``file`` state had the following changes:
|
||||
- The ``show_diff`` option was removed. Please use ``show_changes`` instead.
|
||||
|
||||
Grain Deprecations
|
||||
------------------
|
||||
==================
|
||||
|
||||
For ``smartos`` some grains have been deprecated. These grains will be removed in Neon.
|
||||
|
||||
@ -193,7 +193,7 @@ For ``smartos`` some grains have been deprecated. These grains will be removed i
|
||||
- The ``datacenter`` has been replaced with ``mdata:sdc:datacenter_name`` grain.
|
||||
|
||||
Utils Deprecations
|
||||
------------------
|
||||
==================
|
||||
|
||||
The ``salt.utils.cloud.py`` file had the following change:
|
||||
|
||||
@ -201,7 +201,7 @@ The ``salt.utils.cloud.py`` file had the following change:
|
||||
optional.
|
||||
|
||||
Other Miscellaneous Deprecations
|
||||
--------------------------------
|
||||
================================
|
||||
|
||||
The ``version.py`` file had the following changes:
|
||||
|
||||
|
@ -5,3 +5,4 @@ yappi>=0.8.2
|
||||
--allow-unverified python-neutronclient>2.3.6
|
||||
python-gnupg
|
||||
cherrypy>=3.2.2
|
||||
libnacl
|
||||
|
@ -223,6 +223,7 @@ class SSH(object):
|
||||
else:
|
||||
self.event = None
|
||||
self.opts = opts
|
||||
self.opts['__salt_ssh'] = True
|
||||
if self.opts[u'regen_thin']:
|
||||
self.opts[u'ssh_wipe'] = True
|
||||
if not salt.utils.path.which(u'ssh'):
|
||||
|
@ -2381,7 +2381,7 @@ def _zpool_data(grains):
|
||||
return {}
|
||||
|
||||
# quickly return if NetBSD (ZFS still under development)
|
||||
if salt.utils.is_netbsd():
|
||||
if salt.utils.platform.is_netbsd():
|
||||
return {}
|
||||
|
||||
# quickly return if no zpool and zfs command
|
||||
|
@ -34,7 +34,7 @@ def disks():
|
||||
return _freebsd_geom()
|
||||
elif salt.utils.platform.is_linux():
|
||||
return _linux_disks()
|
||||
elif salt.utils.is_windows():
|
||||
elif salt.utils.platform.is_windows():
|
||||
return _windows_disks()
|
||||
else:
|
||||
log.trace('Disk grain does not support OS')
|
||||
|
@ -125,7 +125,8 @@ def cert(name,
|
||||
salt 'gitlab.example.com' acme.cert dev.example.com "[gitlab.example.com]" test_cert=True renew=14 webroot=/opt/gitlab/embedded/service/gitlab-rails/public
|
||||
'''
|
||||
|
||||
cmd = [LEA, 'certonly', '--quiet']
|
||||
# cmd = [LEA, 'certonly', '--quiet']
|
||||
cmd = [LEA, 'certonly']
|
||||
|
||||
cert_file = _cert_file(name, 'cert')
|
||||
if not __salt__['file.file_exists'](cert_file):
|
||||
|
@ -40,7 +40,7 @@ import salt.utils.path
|
||||
import salt.utils.platform
|
||||
import salt.utils.templates
|
||||
|
||||
if salt.utils.is_windows():
|
||||
if salt.utils.platform.is_windows():
|
||||
import win32file
|
||||
|
||||
# TODO: Check that the passed arguments are correct
|
||||
@ -1063,7 +1063,7 @@ def unzip(zip_file,
|
||||
continue
|
||||
zfile.extract(target, dest, password)
|
||||
if extract_perms:
|
||||
if not salt.utils.is_windows():
|
||||
if not salt.utils.platform.is_windows():
|
||||
perm = zfile.getinfo(target).external_attr >> 16
|
||||
if perm == 0:
|
||||
umask_ = os.umask(0)
|
||||
|
@ -200,7 +200,7 @@ def execute(context=None, lens=None, commands=(), load_path=None):
|
||||
method = METHOD_MAP[cmd]
|
||||
nargs = arg_map[method]
|
||||
|
||||
parts = salt.utils.args.shlex_split(arg)
|
||||
parts = salt.utils.args.shlex_split(arg, posix=False)
|
||||
|
||||
if len(parts) not in nargs:
|
||||
err = '{0} takes {1} args: {2}'.format(method, nargs, parts)
|
||||
|
@ -223,10 +223,23 @@ def create_mount_target(filesystemid,
|
||||
|
||||
client = _get_conn(key=key, keyid=keyid, profile=profile, region=region)
|
||||
|
||||
return client.create_mount_point(FileSystemId=filesystemid,
|
||||
SubnetId=subnetid,
|
||||
IpAddress=ipaddress,
|
||||
SecurityGroups=securitygroups)
|
||||
if ipaddress is None and securitygroups is None:
|
||||
return client.create_mount_target(FileSystemId=filesystemid,
|
||||
SubnetId=subnetid)
|
||||
|
||||
if ipaddress is None:
|
||||
return client.create_mount_target(FileSystemId=filesystemid,
|
||||
SubnetId=subnetid,
|
||||
SecurityGroups=securitygroups)
|
||||
if securitygroups is None:
|
||||
return client.create_mount_target(FileSystemId=filesystemid,
|
||||
SubnetId=subnetid,
|
||||
IpAddress=ipaddress)
|
||||
|
||||
return client.create_mount_target(FileSystemId=filesystemid,
|
||||
SubnetId=subnetid,
|
||||
IpAddress=ipaddress,
|
||||
SecurityGroups=securitygroups)
|
||||
|
||||
|
||||
def create_tags(filesystemid,
|
||||
|
@ -1839,7 +1839,7 @@ def create(image,
|
||||
generate one for you (it will be included in the return data).
|
||||
|
||||
skip_translate
|
||||
This function translates Salt CLI input into the format which
|
||||
This function translates Salt CLI or SLS input into the format which
|
||||
docker-py_ expects. However, in the event that Salt's translation logic
|
||||
fails (due to potential changes in the Docker Remote API, or to bugs in
|
||||
the translation code), this argument can be used to exert granular
|
||||
@ -2107,9 +2107,9 @@ def create(image,
|
||||
- ``dns_search="[foo1.domain.tld, foo2.domain.tld]"``
|
||||
|
||||
domainname
|
||||
Set custom DNS search domains
|
||||
The domain name to use for the container
|
||||
|
||||
Example: ``domainname=domain.tld,domain2.tld``
|
||||
Example: ``domainname=domain.tld``
|
||||
|
||||
entrypoint
|
||||
Entrypoint for the container. Either a string (e.g. ``"mycmd --arg1
|
||||
|
@ -11,112 +11,159 @@ regardless if they are encrypted or not.
|
||||
When generating keys and encrypting passwords use --local when using salt-call for extra
|
||||
security. Also consider using just the salt runner nacl when encrypting pillar passwords.
|
||||
|
||||
:configuration: The following configuration defaults can be
|
||||
define (pillar or config files) Avoid storing private keys in pillars! Ensure master does not have `pillar_opts=True`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# cat /etc/salt/master.d/nacl.conf
|
||||
nacl.config:
|
||||
# NOTE: `key` and `key_file` have been renamed to `sk`, `sk_file`
|
||||
# also `box_type` default changed from secretbox to sealedbox.
|
||||
box_type: sealedbox (default)
|
||||
sk_file: /etc/salt/pki/master/nacl (default)
|
||||
pk_file: /etc/salt/pki/master/nacl.pub (default)
|
||||
sk: None
|
||||
pk: None
|
||||
|
||||
Usage can override the config defaults:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.enc sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
|
||||
|
||||
|
||||
The nacl lib uses 32byte keys, these keys are base64 encoded to make your life more simple.
|
||||
To generate your `key` or `keyfile` you can use:
|
||||
To generate your `sk_file` and `pk_file` use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.keygen keyfile=/root/.nacl
|
||||
salt-call --local nacl.keygen sk_file=/etc/salt/pki/master/nacl
|
||||
# or if you want to work without files.
|
||||
salt-call --local nacl.keygen
|
||||
local:
|
||||
----------
|
||||
pk:
|
||||
/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
|
||||
sk:
|
||||
SVWut5SqNpuPeNzb1b9y6b2eXg2PLIog43GBzp48Sow=
|
||||
|
||||
Now with your key, you can encrypt some data:
|
||||
Now with your keypair, you can encrypt data:
|
||||
|
||||
You have two option, `sealedbox` or `secretbox`.
|
||||
|
||||
SecretBox is data encrypted using private key `pk`. Sealedbox is encrypted using public key `pk`.
|
||||
|
||||
Recommend using Sealedbox because the one way encryption permits developers to encrypt data for source control but not decrypt.
|
||||
Sealedbox only has one key that is for both encryption and decryption.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.enc mypass keyfile=/root/.nacl
|
||||
DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=
|
||||
salt-call --local nacl.enc asecretpass pk=/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
|
||||
tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=
|
||||
|
||||
To decrypt the data:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.dec data='DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=' keyfile=/root/.nacl
|
||||
mypass
|
||||
salt-call --local nacl.dec data='tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=' \
|
||||
sk='SVWut5SqNpuPeNzb1b9y6b2eXg2PLIog43GBzp48Sow='
|
||||
|
||||
The following optional configurations can be defined in the
|
||||
minion or master config. Avoid storing the config in pillars!
|
||||
When the keys are defined in the master config you can use them from the nacl runner
|
||||
without extra parameters:
|
||||
|
||||
.. code-block:: yaml
|
||||
.. code-block:: python
|
||||
|
||||
cat /etc/salt/master.d/nacl.conf
|
||||
# cat /etc/salt/master.d/nacl.conf
|
||||
nacl.config:
|
||||
key: 'cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
||||
keyfile: /root/.nacl
|
||||
|
||||
When the key is defined in the master config you can use it from the nacl runner:
|
||||
sk_file: /etc/salt/pki/master/nacl
|
||||
pk: 'cTIqXwnUiD1ulg4kXsbeCE7/NoeKEzd4nLeYcCFpd9k='
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.enc 'myotherpass'
|
||||
salt-run nacl.enc 'asecretpass'
|
||||
salt-run nacl.dec 'tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58='
|
||||
|
||||
Now you can create a pillar with protected data like:
|
||||
.. code-block:: yam
|
||||
# a salt developers minion could have pillar data that includes a nacl public key
|
||||
nacl.config:
|
||||
pk: '/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0='
|
||||
|
||||
The developer can then use a less-secure system to encrypt data.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.enc apassword
|
||||
|
||||
|
||||
Pillar files can include protected data that the salt master decrypts:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
pillarexample:
|
||||
user: root
|
||||
password: {{ salt.nacl.dec('DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=') }}
|
||||
password1: {{salt.nacl.dec('DRB7Q6/X5gGSRCTpZyxS6hlbWj0llUA+uaVyvou3vJ4=')|json}}
|
||||
cert_key: {{salt.nacl.dec_file('/srv/salt/certs/example.com/key.nacl')|json}}
|
||||
cert_key2: {{salt.nacl.dec_file('salt:///certs/example.com/key.nacl')|json}}
|
||||
|
||||
Or do something interesting with grains like:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
salt-call nacl.enc minionname:dbrole
|
||||
AL24Z2C5OlkReer3DuQTFdrNLchLuz3NGIhGjZkLtKRYry/b/CksWM8O9yskLwH2AGVLoEXI5jAa
|
||||
|
||||
salt minionname grains.setval role 'AL24Z2C5OlkReer3DuQTFdrNLchLuz3NGIhGjZkLtKRYry/b/CksWM8O9yskLwH2AGVLoEXI5jAa'
|
||||
|
||||
{%- set r = grains.get('role') %}
|
||||
{%- set role = None %}
|
||||
{%- if r and 'nacl.dec' in salt %}
|
||||
{%- set r = salt['nacl.dec'](r,keyfile='/root/.nacl').split(':') %}
|
||||
{%- if opts['id'] == r[0] %}
|
||||
{%- set role = r[1] %}
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
base:
|
||||
{%- if role %}
|
||||
'{{ opts['id'] }}':
|
||||
- {{ role }}
|
||||
{%- endif %}
|
||||
|
||||
Multi-line text items like certificates require a bit of extra work. You have to strip the new lines
|
||||
and replace them with '/n' characters. Certificates specifically require some leading white space when
|
||||
calling nacl.enc so that the '--' in the first line (commonly -----BEGIN CERTIFICATE-----) doesn't get
|
||||
interpreted as an argument to nacl.enc. For instance if you have a certificate file that lives in cert.crt:
|
||||
Larger files like certificates can be encrypted with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cert=$(cat cert.crt |awk '{printf "%s\\n",$0} END {print ""}'); salt-run nacl.enc " $cert"
|
||||
salt-call nacl.enc_file /tmp/cert.crt out=/tmp/cert.nacl
|
||||
# or more advanced
|
||||
cert=$(cat /tmp/cert.crt)
|
||||
salt-call --out=newline_values_only nacl.enc_pub data="$cert" > /tmp/cert.nacl
|
||||
|
||||
Pillar data should look the same, even though the secret will be quite long. However, when calling
|
||||
multiline encrypted secrets from pillar in a state, use the following format to avoid issues with /n
|
||||
creating extra whitespace at the beginning of each line in the cert file:
|
||||
In pillars rended with jinja be sure to include `|json` so line breaks are encoded:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
secret.txt:
|
||||
file.managed:
|
||||
- template: jinja
|
||||
- user: user
|
||||
- group: group
|
||||
- mode: 700
|
||||
- contents: "{{- salt['pillar.get']('secret') }}"
|
||||
cert: "{{salt.nacl.dec('S2uogToXkgENz9...085KYt')|json}}"
|
||||
|
||||
In states rendered with jinja it is also good pratice to include `|json`:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{sls}} private key:
|
||||
file.managed:
|
||||
- name: /etc/ssl/private/cert.key
|
||||
- mode: 700
|
||||
- contents: "{{pillar['pillarexample']['cert_key']|json}}"
|
||||
|
||||
|
||||
Optional small program to encrypt data without needing salt modules.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/bin/python3
|
||||
import sys, base64, libnacl.sealed
|
||||
pk = base64.b64decode('YOURPUBKEY')
|
||||
b = libnacl.sealed.SealedBox(pk)
|
||||
data = sys.stdin.buffer.read()
|
||||
print(base64.b64encode(b.encrypt(data)).decode())
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
echo 'apassword' | nacl_enc.py
|
||||
|
||||
The '{{-' will tell jinja to strip the whitespace from the beginning of each of the new lines.
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
import base64
|
||||
import os
|
||||
import salt.utils.files
|
||||
import salt.utils
|
||||
import salt.utils.win_functions
|
||||
import salt.utils.win_dacl
|
||||
import salt.syspaths
|
||||
|
||||
|
||||
REQ_ERROR = None
|
||||
try:
|
||||
import libnacl.secret
|
||||
import libnacl.sealed
|
||||
except (ImportError, OSError) as e:
|
||||
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package'
|
||||
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.'
|
||||
|
||||
__virtualname__ = 'nacl'
|
||||
|
||||
@ -130,91 +177,294 @@ def _get_config(**kwargs):
|
||||
Return configuration
|
||||
'''
|
||||
config = {
|
||||
'key': None,
|
||||
'keyfile': None,
|
||||
'box_type': 'sealedbox',
|
||||
'sk': None,
|
||||
'sk_file': '/etc/salt/pki/master/nacl',
|
||||
'pk': None,
|
||||
'pk_file': '/etc/salt/pki/master/nacl.pub',
|
||||
}
|
||||
config_key = '{0}.config'.format(__virtualname__)
|
||||
config.update(__salt__['config.get'](config_key, {}))
|
||||
for k in set(config) & set(kwargs):
|
||||
try:
|
||||
config.update(__salt__['config.get'](config_key, {}))
|
||||
except (NameError, KeyError) as e:
|
||||
# likly using salt-run so fallback to __opts__
|
||||
config.update(__opts__.get(config_key, {}))
|
||||
# pylint: disable=C0201
|
||||
for k in set(config.keys()) & set(kwargs.keys()):
|
||||
config[k] = kwargs[k]
|
||||
return config
|
||||
|
||||
|
||||
def _get_key(rstrip_newline=True, **kwargs):
|
||||
def _get_sk(**kwargs):
|
||||
'''
|
||||
Return key
|
||||
Return sk
|
||||
'''
|
||||
config = _get_config(**kwargs)
|
||||
key = config['key']
|
||||
keyfile = config['keyfile']
|
||||
if not key and keyfile:
|
||||
if not os.path.isfile(keyfile):
|
||||
raise Exception('file not found: {0}'.format(keyfile))
|
||||
with salt.utils.files.fopen(keyfile, 'rb') as keyf:
|
||||
key = keyf.read()
|
||||
key = config['sk']
|
||||
sk_file = config['sk_file']
|
||||
if not key and sk_file:
|
||||
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||
key = str(keyf.read()).rstrip('\n')
|
||||
if key is None:
|
||||
raise Exception('no key found')
|
||||
key = str(key)
|
||||
if rstrip_newline:
|
||||
key = key.rstrip('\n')
|
||||
return key
|
||||
raise Exception('no key or sk_file found')
|
||||
return base64.b64decode(key)
|
||||
|
||||
|
||||
def keygen(keyfile=None):
|
||||
def _get_pk(**kwargs):
|
||||
'''
|
||||
Use libnacl to generate a private key
|
||||
Return pk
|
||||
'''
|
||||
config = _get_config(**kwargs)
|
||||
pubkey = config['pk']
|
||||
pk_file = config['pk_file']
|
||||
if not pubkey and pk_file:
|
||||
with salt.utils.fopen(pk_file, 'rb') as keyf:
|
||||
pubkey = str(keyf.read()).rstrip('\n')
|
||||
if pubkey is None:
|
||||
raise Exception('no pubkey or pk_file found')
|
||||
pubkey = str(pubkey)
|
||||
return base64.b64decode(pubkey)
|
||||
|
||||
|
||||
def keygen(sk_file=None, pk_file=None):
|
||||
'''
|
||||
Use libnacl to generate a keypair.
|
||||
|
||||
If no `sk_file` is defined return a keypair.
|
||||
|
||||
If only the `sk_file` is defined `pk_file` will use the same name with a postfix `.pub`.
|
||||
|
||||
When the `sk_file` is already existing, but `pk_file` is not. The `pk_file` will be generated
|
||||
using the `sk_file`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.keygen
|
||||
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
|
||||
salt-call --local nacl.keygen
|
||||
salt-call --local nacl.keygen keyfile=/root/.nacl
|
||||
salt-call --local --out=newline_values_only nacl.keygen > /root/.nacl
|
||||
'''
|
||||
b = libnacl.secret.SecretBox()
|
||||
key = b.sk
|
||||
key = base64.b64encode(key)
|
||||
if keyfile:
|
||||
if os.path.isfile(keyfile):
|
||||
raise Exception('file already found: {0}'.format(keyfile))
|
||||
with salt.utils.files.fopen(keyfile, 'w') as keyf:
|
||||
keyf.write(key)
|
||||
return 'saved: {0}'.format(keyfile)
|
||||
return key
|
||||
if sk_file is None:
|
||||
kp = libnacl.public.SecretKey()
|
||||
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
|
||||
|
||||
if pk_file is None:
|
||||
pk_file = '{0}.pub'.format(sk_file)
|
||||
|
||||
if sk_file and pk_file is None:
|
||||
if not os.path.isfile(sk_file):
|
||||
kp = libnacl.public.SecretKey()
|
||||
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.sk))
|
||||
if salt.utils.is_windows():
|
||||
cur_user = salt.utils.win_functions.get_current_user()
|
||||
salt.utils.win_dacl.set_owner(sk_file, cur_user)
|
||||
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
|
||||
else:
|
||||
# chmod 0600 file
|
||||
os.chmod(sk_file, 1536)
|
||||
return 'saved sk_file: {0}'.format(sk_file)
|
||||
else:
|
||||
raise Exception('sk_file:{0} already exist.'.format(sk_file))
|
||||
|
||||
if sk_file is None and pk_file:
|
||||
raise Exception('sk_file: Must be set inorder to generate a public key.')
|
||||
|
||||
if os.path.isfile(sk_file) and os.path.isfile(pk_file):
|
||||
raise Exception('sk_file:{0} and pk_file:{1} already exist.'.format(sk_file, pk_file))
|
||||
|
||||
if os.path.isfile(sk_file) and not os.path.isfile(pk_file):
|
||||
# generate pk using the sk
|
||||
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||
sk = str(keyf.read()).rstrip('\n')
|
||||
sk = base64.b64decode(sk)
|
||||
kp = libnacl.public.SecretKey(sk)
|
||||
with salt.utils.fopen(pk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.pk))
|
||||
return 'saved pk_file: {0}'.format(pk_file)
|
||||
|
||||
kp = libnacl.public.SecretKey()
|
||||
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.sk))
|
||||
if salt.utils.is_windows():
|
||||
cur_user = salt.utils.win_functions.get_current_user()
|
||||
salt.utils.win_dacl.set_owner(sk_file, cur_user)
|
||||
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
|
||||
else:
|
||||
# chmod 0600 file
|
||||
os.chmod(sk_file, 1536)
|
||||
with salt.utils.fopen(pk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.pk))
|
||||
return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file)
|
||||
|
||||
|
||||
def enc(data, **kwargs):
|
||||
'''
|
||||
Takes a key generated from `nacl.keygen` and encrypt some data.
|
||||
Alias to `{box_type}_encrypt`
|
||||
|
||||
box_type: secretbox, sealedbox(default)
|
||||
'''
|
||||
box_type = _get_config(**kwargs)['box_type']
|
||||
if box_type == 'sealedbox':
|
||||
return sealedbox_encrypt(data, **kwargs)
|
||||
if box_type == 'secretbox':
|
||||
return secretbox_encrypt(data, **kwargs)
|
||||
return sealedbox_encrypt(data, **kwargs)
|
||||
|
||||
|
||||
def enc_file(name, out=None, **kwargs):
|
||||
'''
|
||||
This is a helper function to encrypt a file and return its contents.
|
||||
|
||||
You can provide an optional output file using `out`
|
||||
|
||||
`name` can be a local file or when not using `salt-run` can be a url like `salt://`, `https://` etc.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.enc datatoenc
|
||||
salt-call --local nacl.enc datatoenc keyfile=/root/.nacl
|
||||
salt-call --local nacl.enc datatoenc key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
||||
salt-run nacl.enc_file name=/tmp/id_rsa
|
||||
salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert
|
||||
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
|
||||
sk_file=/etc/salt/pki/master/nacl.pub
|
||||
'''
|
||||
key = _get_key(**kwargs)
|
||||
sk = base64.b64decode(key)
|
||||
b = libnacl.secret.SecretBox(sk)
|
||||
return base64.b64encode(b.encrypt(data))
|
||||
try:
|
||||
data = __salt__['cp.get_file_str'](name)
|
||||
except Exception as e:
|
||||
# likly using salt-run so fallback to local filesystem
|
||||
with salt.utils.fopen(name, 'rb') as f:
|
||||
data = f.read()
|
||||
d = enc(data, **kwargs)
|
||||
if out:
|
||||
if os.path.isfile(out):
|
||||
raise Exception('file:{0} already exist.'.format(out))
|
||||
with salt.utils.fopen(out, 'wb') as f:
|
||||
f.write(d)
|
||||
return 'Wrote: {0}'.format(out)
|
||||
return d
|
||||
|
||||
|
||||
def dec(data, **kwargs):
|
||||
'''
|
||||
Takes a key generated from `nacl.keygen` and decrypt some data.
|
||||
Alias to `{box_type}_decrypt`
|
||||
|
||||
box_type: secretbox, sealedbox(default)
|
||||
'''
|
||||
box_type = _get_config(**kwargs)['box_type']
|
||||
if box_type == 'sealedbox':
|
||||
return sealedbox_decrypt(data, **kwargs)
|
||||
if box_type == 'secretbox':
|
||||
return secretbox_decrypt(data, **kwargs)
|
||||
return sealedbox_decrypt(data, **kwargs)
|
||||
|
||||
|
||||
def dec_file(name, out=None, **kwargs):
|
||||
'''
|
||||
This is a helper function to decrypt a file and return its contents.
|
||||
|
||||
You can provide an optional output file using `out`
|
||||
|
||||
`name` can be a local file or when not using `salt-run` can be a url like `salt://`, `https://` etc.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.dec pEXHQM6cuaF7A=
|
||||
salt-call --local nacl.dec data='pEXHQM6cuaF7A=' keyfile=/root/.nacl
|
||||
salt-call --local nacl.dec data='pEXHQM6cuaF7A=' key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
||||
salt-run nacl.dec_file name=/tmp/id_rsa.nacl
|
||||
salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa
|
||||
salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \
|
||||
sk_file=/etc/salt/pki/master/nacl.pub
|
||||
'''
|
||||
key = _get_key(**kwargs)
|
||||
sk = base64.b64decode(key)
|
||||
b = libnacl.secret.SecretBox(key=sk)
|
||||
try:
|
||||
data = __salt__['cp.get_file_str'](name)
|
||||
except Exception as e:
|
||||
# likly using salt-run so fallback to local filesystem
|
||||
with salt.utils.fopen(name, 'rb') as f:
|
||||
data = f.read()
|
||||
d = dec(data, **kwargs)
|
||||
if out:
|
||||
if os.path.isfile(out):
|
||||
raise Exception('file:{0} already exist.'.format(out))
|
||||
with salt.utils.fopen(out, 'wb') as f:
|
||||
f.write(d)
|
||||
return 'Wrote: {0}'.format(out)
|
||||
return d
|
||||
|
||||
|
||||
def sealedbox_encrypt(data, **kwargs):
|
||||
'''
|
||||
Encrypt data using a public key generated from `nacl.keygen`.
|
||||
The encryptd data can be decrypted using `nacl.sealedbox_decrypt` only with the secret key.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.sealedbox_encrypt datatoenc
|
||||
salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub
|
||||
salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ='
|
||||
'''
|
||||
pk = _get_pk(**kwargs)
|
||||
b = libnacl.sealed.SealedBox(pk)
|
||||
return base64.b64encode(b.encrypt(data))
|
||||
|
||||
|
||||
def sealedbox_decrypt(data, **kwargs):
|
||||
'''
|
||||
Decrypt data using a secret key that was encrypted using a public key with `nacl.sealedbox_encrypt`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.sealedbox_decrypt pEXHQM6cuaF7A=
|
||||
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
|
||||
'''
|
||||
if data is None:
|
||||
return None
|
||||
sk = _get_sk(**kwargs)
|
||||
keypair = libnacl.public.SecretKey(sk)
|
||||
b = libnacl.sealed.SealedBox(keypair)
|
||||
return b.decrypt(base64.b64decode(data))
|
||||
|
||||
|
||||
def secretbox_encrypt(data, **kwargs):
|
||||
'''
|
||||
Encrypt data using a secret key generated from `nacl.keygen`.
|
||||
The same secret key can be used to decrypt the data using `nacl.secretbox_decrypt`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.secretbox_encrypt datatoenc
|
||||
salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
|
||||
'''
|
||||
sk = _get_sk(**kwargs)
|
||||
b = libnacl.secret.SecretBox(sk)
|
||||
return base64.b64encode(b.encrypt(data))
|
||||
|
||||
|
||||
def secretbox_decrypt(data, **kwargs):
|
||||
'''
|
||||
Decrypt data that was encrypted using `nacl.secretbox_encrypt` using the secret key
|
||||
that was generated from `nacl.keygen`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.secretbox_decrypt pEXHQM6cuaF7A=
|
||||
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
|
||||
'''
|
||||
if data is None:
|
||||
return None
|
||||
key = _get_sk(**kwargs)
|
||||
b = libnacl.secret.SecretBox(key=key)
|
||||
return b.decrypt(base64.b64decode(data))
|
||||
|
@ -257,8 +257,8 @@ def items(*args, **kwargs):
|
||||
__opts__,
|
||||
__grains__,
|
||||
__opts__['id'],
|
||||
pillar_override=kwargs.get('pillar'),
|
||||
pillarenv=kwargs.get('pillarenv') or __opts__['pillarenv'])
|
||||
pillar_override=pillar_override,
|
||||
pillarenv=pillarenv)
|
||||
|
||||
return pillar.compile_pillar()
|
||||
|
||||
|
@ -611,11 +611,11 @@ def set_user_tags(name, tags, runas=None):
|
||||
if runas is None and not salt.utils.platform.is_windows():
|
||||
runas = salt.utils.get_user()
|
||||
|
||||
if tags and isinstance(tags, (list, tuple)):
|
||||
tags = ' '.join(tags)
|
||||
if not isinstance(tags, (list, tuple)):
|
||||
tags = [tags]
|
||||
|
||||
res = __salt__['cmd.run_all'](
|
||||
[RABBITMQCTL, 'set_user_tags', name, tags],
|
||||
[RABBITMQCTL, 'set_user_tags', name] + list(tags),
|
||||
runas=runas,
|
||||
python_shell=False)
|
||||
msg = "Tag(s) set"
|
||||
|
@ -18,6 +18,7 @@ import salt.utils.files
|
||||
import salt.utils.stringutils
|
||||
import salt.utils.templates
|
||||
import salt.utils.validate.net
|
||||
from salt.exceptions import CommandExecutionError
|
||||
from salt.ext import six
|
||||
|
||||
# Set up logging
|
||||
@ -667,14 +668,24 @@ def _parse_settings_eth(opts, iface_type, enabled, iface):
|
||||
bypassfirewall = False
|
||||
else:
|
||||
_raise_error_iface(iface, opts[opt], valid)
|
||||
|
||||
bridgectls = [
|
||||
'net.bridge.bridge-nf-call-ip6tables',
|
||||
'net.bridge.bridge-nf-call-iptables',
|
||||
'net.bridge.bridge-nf-call-arptables',
|
||||
]
|
||||
|
||||
if bypassfirewall:
|
||||
__salt__['sysctl.persist']('net.bridge.bridge-nf-call-ip6tables', '0')
|
||||
__salt__['sysctl.persist']('net.bridge.bridge-nf-call-iptables', '0')
|
||||
__salt__['sysctl.persist']('net.bridge.bridge-nf-call-arptables', '0')
|
||||
sysctl_value = 0
|
||||
else:
|
||||
__salt__['sysctl.persist']('net.bridge.bridge-nf-call-ip6tables', '1')
|
||||
__salt__['sysctl.persist']('net.bridge.bridge-nf-call-iptables', '1')
|
||||
__salt__['sysctl.persist']('net.bridge.bridge-nf-call-arptables', '1')
|
||||
sysctl_value = 1
|
||||
|
||||
for sysctl in bridgectls:
|
||||
try:
|
||||
__salt__['sysctl.persist'](sysctl, sysctl_value)
|
||||
except CommandExecutionError:
|
||||
log.warning('Failed to set sysctl: {0}'.format(sysctl))
|
||||
|
||||
else:
|
||||
if 'bridge' in opts:
|
||||
result['bridge'] = opts['bridge']
|
||||
|
@ -856,7 +856,7 @@ def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443,
|
||||
|
||||
new_cert_bindings = list_cert_bindings(site)
|
||||
|
||||
if binding_info not in new_cert_bindings(site):
|
||||
if binding_info not in new_cert_bindings:
|
||||
log.error('Binding not present: {0}'.format(binding_info))
|
||||
return False
|
||||
|
||||
|
@ -580,13 +580,21 @@ import signal
|
||||
import tarfile
|
||||
from multiprocessing import Process, Pipe
|
||||
|
||||
# Import third-party libs
|
||||
# pylint: disable=import-error
|
||||
import cherrypy # pylint: disable=3rd-party-module-not-gated
|
||||
import yaml
|
||||
from salt.ext import six
|
||||
# pylint: enable=import-error
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Import third-party libs
|
||||
# pylint: disable=import-error, 3rd-party-module-not-gated
|
||||
import cherrypy
|
||||
try:
|
||||
from cherrypy.lib import cpstats
|
||||
except ImportError:
|
||||
cpstats = None
|
||||
logger.warn('Import of cherrypy.cpstats failed. '
|
||||
'Possible upstream bug: '
|
||||
'https://github.com/cherrypy/cherrypy/issues/1444')
|
||||
|
||||
import yaml
|
||||
# pylint: enable=import-error, 3rd-party-module-not-gated
|
||||
|
||||
# Import Salt libs
|
||||
import salt
|
||||
@ -594,12 +602,11 @@ import salt.auth
|
||||
import salt.utils
|
||||
import salt.utils.event
|
||||
import salt.utils.stringutils
|
||||
from salt.ext import six
|
||||
|
||||
# Import salt-api libs
|
||||
import salt.netapi
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Imports related to websocket
|
||||
try:
|
||||
from .tools import websockets
|
||||
@ -2717,13 +2724,6 @@ class Stats(object):
|
||||
:status 406: |406|
|
||||
'''
|
||||
if hasattr(logging, 'statistics'):
|
||||
# Late import
|
||||
try:
|
||||
from cherrypy.lib import cpstats
|
||||
except ImportError:
|
||||
logger.error('Import of cherrypy.cpstats failed. Possible '
|
||||
'upstream bug here: https://github.com/cherrypy/cherrypy/issues/1444')
|
||||
return {}
|
||||
return cpstats.extrapolate_statistics(logging.statistics)
|
||||
|
||||
return {}
|
||||
@ -2843,13 +2843,14 @@ class API(object):
|
||||
'tools.trailing_slash.on': True,
|
||||
'tools.gzip.on': True,
|
||||
|
||||
'tools.cpstats.on': self.apiopts.get('collect_stats', False),
|
||||
|
||||
'tools.html_override.on': True,
|
||||
'tools.cors_tool.on': True,
|
||||
},
|
||||
}
|
||||
|
||||
if cpstats and self.apiopts.get('collect_stats', False):
|
||||
conf['/']['tools.cpstats.on'] = True
|
||||
|
||||
if 'favicon' in self.apiopts:
|
||||
conf['/favicon.ico'] = {
|
||||
'tools.staticfile.on': True,
|
||||
|
@ -523,8 +523,7 @@ class BaseSaltAPIHandler(tornado.web.RequestHandler, SaltClientsMixIn): # pylin
|
||||
|
||||
try:
|
||||
# Use cgi.parse_header to correctly separate parameters from value
|
||||
header = cgi.parse_header(self.request.headers['Content-Type'])
|
||||
value, parameters = header
|
||||
value, parameters = cgi.parse_header(self.request.headers['Content-Type'])
|
||||
return ct_in_map[value](tornado.escape.native_str(data))
|
||||
except KeyError:
|
||||
self.send_error(406)
|
||||
@ -538,7 +537,7 @@ class BaseSaltAPIHandler(tornado.web.RequestHandler, SaltClientsMixIn): # pylin
|
||||
if not self.request.body:
|
||||
return
|
||||
data = self.deserialize(self.request.body)
|
||||
self.raw_data = copy(data)
|
||||
self.request_payload = copy(data)
|
||||
|
||||
if data and 'arg' in data and not isinstance(data['arg'], list):
|
||||
data['arg'] = [data['arg']]
|
||||
@ -696,15 +695,13 @@ class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223
|
||||
}}
|
||||
'''
|
||||
try:
|
||||
request_payload = self.deserialize(self.request.body)
|
||||
|
||||
if not isinstance(request_payload, dict):
|
||||
if not isinstance(self.request_payload, dict):
|
||||
self.send_error(400)
|
||||
return
|
||||
|
||||
creds = {'username': request_payload['username'],
|
||||
'password': request_payload['password'],
|
||||
'eauth': request_payload['eauth'],
|
||||
creds = {'username': self.request_payload['username'],
|
||||
'password': self.request_payload['password'],
|
||||
'eauth': self.request_payload['eauth'],
|
||||
}
|
||||
# if any of the args are missing, its a bad request
|
||||
except KeyError:
|
||||
@ -1641,7 +1638,7 @@ class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223
|
||||
value = value[0]
|
||||
arguments[argname] = value
|
||||
ret = self.event.fire_event({
|
||||
'post': self.raw_data,
|
||||
'post': self.request_payload,
|
||||
'get': arguments,
|
||||
# In Tornado >= v4.0.3, the headers come
|
||||
# back as an HTTPHeaders instance, which
|
||||
|
@ -174,7 +174,7 @@ def get_printout(out, opts=None, **kwargs):
|
||||
else:
|
||||
if opts.get('force_color', False):
|
||||
opts['color'] = True
|
||||
elif opts.get('no_color', False) or salt.utils.is_windows():
|
||||
elif opts.get('no_color', False) or salt.utils.platform.is_windows():
|
||||
opts['color'] = False
|
||||
else:
|
||||
pass
|
||||
|
@ -14,16 +14,13 @@ import os
|
||||
|
||||
# Import Salt libs
|
||||
import salt.cloud
|
||||
import salt.utils.args
|
||||
from salt.exceptions import SaltCloudConfigError
|
||||
|
||||
# Get logging started
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _filter_kwargs(k):
|
||||
return dict((x, k[x]) for x in k if not x.startswith('__'))
|
||||
|
||||
|
||||
def _get_client():
|
||||
'''
|
||||
Return cloud client
|
||||
@ -114,7 +111,7 @@ def profile(prof=None, instances=None, opts=None, **kwargs):
|
||||
client = _get_client()
|
||||
if isinstance(opts, dict):
|
||||
client.opts.update(opts)
|
||||
info = client.profile(prof, instances, **_filter_kwargs(kwargs))
|
||||
info = client.profile(prof, instances, **salt.utils.args.clean_kwargs(**kwargs))
|
||||
return info
|
||||
|
||||
|
||||
@ -123,7 +120,7 @@ def map_run(path=None, **kwargs):
|
||||
Execute a salt cloud map file
|
||||
'''
|
||||
client = _get_client()
|
||||
info = client.map_run(path, **_filter_kwargs(kwargs))
|
||||
info = client.map_run(path, **salt.utils.args.clean_kwargs(**kwargs))
|
||||
return info
|
||||
|
||||
|
||||
@ -154,7 +151,14 @@ def action(func=None,
|
||||
info = {}
|
||||
client = _get_client()
|
||||
try:
|
||||
info = client.action(func, cloudmap, instances, provider, instance, **_filter_kwargs(kwargs))
|
||||
info = client.action(
|
||||
func,
|
||||
cloudmap,
|
||||
instances,
|
||||
provider,
|
||||
instance,
|
||||
**salt.utils.args.clean_kwargs(**kwargs)
|
||||
)
|
||||
except SaltCloudConfigError as err:
|
||||
log.error(err)
|
||||
return info
|
||||
@ -172,12 +176,8 @@ def create(provider, instances, opts=None, **kwargs):
|
||||
image=ami-1624987f size='t1.micro' ssh_username=ec2-user \
|
||||
securitygroup=default delvol_on_destroy=True
|
||||
'''
|
||||
create_kwargs = {}
|
||||
for kwarg in kwargs:
|
||||
if not kwarg.startswith('__'):
|
||||
create_kwargs[kwarg] = kwargs[kwarg]
|
||||
client = _get_client()
|
||||
if isinstance(opts, dict):
|
||||
client.opts.update(opts)
|
||||
info = client.create(provider, instances, **create_kwargs)
|
||||
info = client.create(provider, instances, **salt.utils.args.clean_kwargs(**kwargs))
|
||||
return info
|
||||
|
@ -22,6 +22,7 @@ from salt.ext.six.moves.urllib.request import urlopen as _urlopen # pylint: dis
|
||||
# Import salt libs
|
||||
import salt.key
|
||||
import salt.utils
|
||||
import salt.utils.compat
|
||||
import salt.utils.files
|
||||
import salt.utils.minions
|
||||
import salt.client
|
||||
@ -670,7 +671,7 @@ def versions():
|
||||
ver_diff = -2
|
||||
else:
|
||||
minion_version = salt.version.SaltStackVersion.parse(minions[minion])
|
||||
ver_diff = cmp(minion_version, master_version)
|
||||
ver_diff = salt.utils.compat.cmp(minion_version, master_version)
|
||||
|
||||
if ver_diff not in version_status:
|
||||
version_status[ver_diff] = {}
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
This runner helps create encrypted passwords that can be included in pillars.
|
||||
This module helps include encrypted passwords in pillars, grains and salt state files.
|
||||
|
||||
:depends: libnacl, https://github.com/saltstack/libnacl
|
||||
|
||||
@ -8,35 +8,162 @@ This is often useful if you wish to store your pillars in source control or
|
||||
share your pillar data with others that you trust. I don't advise making your pillars public
|
||||
regardless if they are encrypted or not.
|
||||
|
||||
The following configurations can be defined in the master config
|
||||
so your users can create encrypted passwords using the runner nacl:
|
||||
When generating keys and encrypting passwords use --local when using salt-call for extra
|
||||
security. Also consider using just the salt runner nacl when encrypting pillar passwords.
|
||||
|
||||
:configuration: The following configuration defaults can be
|
||||
define (pillar or config files) Avoid storing private keys in pillars! Ensure master does not have `pillar_opts=True`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# cat /etc/salt/master.d/nacl.conf
|
||||
nacl.config:
|
||||
# NOTE: `key` and `key_file` have been renamed to `sk`, `sk_file`
|
||||
# also `box_type` default changed from secretbox to sealedbox.
|
||||
box_type: sealedbox (default)
|
||||
sk_file: /etc/salt/pki/master/nacl (default)
|
||||
pk_file: /etc/salt/pki/master/nacl.pub (default)
|
||||
sk: None
|
||||
pk: None
|
||||
|
||||
Usage can override the config defaults:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.enc sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
|
||||
|
||||
|
||||
The nacl lib uses 32byte keys, these keys are base64 encoded to make your life more simple.
|
||||
To generate your `sk_file` and `pk_file` use:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cat /etc/salt/master.d/nacl.conf
|
||||
salt-call --local nacl.keygen sk_file=/etc/salt/pki/master/nacl
|
||||
# or if you want to work without files.
|
||||
salt-call --local nacl.keygen
|
||||
local:
|
||||
----------
|
||||
pk:
|
||||
/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
|
||||
sk:
|
||||
SVWut5SqNpuPeNzb1b9y6b2eXg2PLIog43GBzp48Sow=
|
||||
|
||||
Now with your keypair, you can encrypt data:
|
||||
|
||||
You have two option, `sealedbox` or `secretbox`.
|
||||
|
||||
SecretBox is data encrypted using private key `pk`. Sealedbox is encrypted using public key `pk`.
|
||||
|
||||
Recommend using Sealedbox because the one way encryption permits developers to encrypt data for source control but not decrypt.
|
||||
Sealedbox only has one key that is for both encryption and decryption.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.enc asecretpass pk=/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
|
||||
tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=
|
||||
|
||||
To decrypt the data:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.dec data='tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=' \
|
||||
sk='SVWut5SqNpuPeNzb1b9y6b2eXg2PLIog43GBzp48Sow='
|
||||
|
||||
When the keys are defined in the master config you can use them from the nacl runner
|
||||
without extra parameters:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# cat /etc/salt/master.d/nacl.conf
|
||||
nacl.config:
|
||||
key: 'cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
||||
keyfile: /root/.nacl
|
||||
|
||||
Now with the config in the master you can use the runner nacl like:
|
||||
sk_file: /etc/salt/pki/master/nacl
|
||||
pk: 'cTIqXwnUiD1ulg4kXsbeCE7/NoeKEzd4nLeYcCFpd9k='
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.enc 'data'
|
||||
salt-run nacl.enc 'asecretpass'
|
||||
salt-run nacl.dec 'tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58='
|
||||
|
||||
.. code-block:: yam
|
||||
# a salt developers minion could have pillar data that includes a nacl public key
|
||||
nacl.config:
|
||||
pk: '/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0='
|
||||
|
||||
The developer can then use a less-secure system to encrypt data.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call --local nacl.enc apassword
|
||||
|
||||
|
||||
Pillar files can include protected data that the salt master decrypts:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
pillarexample:
|
||||
user: root
|
||||
password1: {{salt.nacl.dec('DRB7Q6/X5gGSRCTpZyxS6hlbWj0llUA+uaVyvou3vJ4=')|json}}
|
||||
cert_key: {{salt.nacl.dec_file('/srv/salt/certs/example.com/key.nacl')|json}}
|
||||
cert_key2: {{salt.nacl.dec_file('salt:///certs/example.com/key.nacl')|json}}
|
||||
|
||||
Larger files like certificates can be encrypted with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.enc_file /tmp/cert.crt out=/tmp/cert.nacl
|
||||
# or more advanced
|
||||
cert=$(cat /tmp/cert.crt)
|
||||
salt-call --out=newline_values_only nacl.enc_pub data="$cert" > /tmp/cert.nacl
|
||||
|
||||
In pillars rended with jinja be sure to include `|json` so line breaks are encoded:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
cert: "{{salt.nacl.dec('S2uogToXkgENz9...085KYt')|json}}"
|
||||
|
||||
In states rendered with jinja it is also good pratice to include `|json`:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{sls}} private key:
|
||||
file.managed:
|
||||
- name: /etc/ssl/private/cert.key
|
||||
- mode: 700
|
||||
- contents: "{{pillar['pillarexample']['cert_key']|json}}"
|
||||
|
||||
|
||||
Optional small program to encrypt data without needing salt modules.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
#!/bin/python3
|
||||
import sys, base64, libnacl.sealed
|
||||
pk = base64.b64decode('YOURPUBKEY')
|
||||
b = libnacl.sealed.SealedBox(pk)
|
||||
data = sys.stdin.buffer.read()
|
||||
print(base64.b64encode(b.encrypt(data)).decode())
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
echo 'apassword' | nacl_enc.py
|
||||
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
import base64
|
||||
import os
|
||||
import salt.utils.files
|
||||
import salt.utils
|
||||
import salt.utils.win_functions
|
||||
import salt.utils.win_dacl
|
||||
import salt.syspaths
|
||||
|
||||
|
||||
REQ_ERROR = None
|
||||
try:
|
||||
import libnacl.secret
|
||||
except ImportError as e:
|
||||
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package'
|
||||
import libnacl.sealed
|
||||
except (ImportError, OSError) as e:
|
||||
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.'
|
||||
|
||||
__virtualname__ = 'nacl'
|
||||
|
||||
@ -50,91 +177,294 @@ def _get_config(**kwargs):
|
||||
Return configuration
|
||||
'''
|
||||
config = {
|
||||
'key': None,
|
||||
'keyfile': None,
|
||||
'box_type': 'sealedbox',
|
||||
'sk': None,
|
||||
'sk_file': '/etc/salt/pki/master/nacl',
|
||||
'pk': None,
|
||||
'pk_file': '/etc/salt/pki/master/nacl.pub',
|
||||
}
|
||||
config_key = '{0}.config'.format(__virtualname__)
|
||||
config.update(__opts__.get(config_key, {}))
|
||||
for k in set(config) & set(kwargs):
|
||||
try:
|
||||
config.update(__salt__['config.get'](config_key, {}))
|
||||
except (NameError, KeyError) as e:
|
||||
# likly using salt-run so fallback to __opts__
|
||||
config.update(__opts__.get(config_key, {}))
|
||||
# pylint: disable=C0201
|
||||
for k in set(config.keys()) & set(kwargs.keys()):
|
||||
config[k] = kwargs[k]
|
||||
return config
|
||||
|
||||
|
||||
def _get_key(rstrip_newline=True, **kwargs):
|
||||
def _get_sk(**kwargs):
|
||||
'''
|
||||
Return key
|
||||
Return sk
|
||||
'''
|
||||
config = _get_config(**kwargs)
|
||||
key = config['key']
|
||||
keyfile = config['keyfile']
|
||||
if not key and keyfile:
|
||||
if not os.path.isfile(keyfile):
|
||||
raise Exception('file not found: {0}'.format(keyfile))
|
||||
with salt.utils.files.fopen(keyfile, 'rb') as keyf:
|
||||
key = keyf.read()
|
||||
key = config['sk']
|
||||
sk_file = config['sk_file']
|
||||
if not key and sk_file:
|
||||
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||
key = str(keyf.read()).rstrip('\n')
|
||||
if key is None:
|
||||
raise Exception('no key found')
|
||||
key = str(key)
|
||||
if rstrip_newline:
|
||||
key = key.rstrip('\n')
|
||||
return key
|
||||
raise Exception('no key or sk_file found')
|
||||
return base64.b64decode(key)
|
||||
|
||||
|
||||
def keygen(keyfile=None):
|
||||
def _get_pk(**kwargs):
|
||||
'''
|
||||
Use libnacl to generate a private key
|
||||
Return pk
|
||||
'''
|
||||
config = _get_config(**kwargs)
|
||||
pubkey = config['pk']
|
||||
pk_file = config['pk_file']
|
||||
if not pubkey and pk_file:
|
||||
with salt.utils.fopen(pk_file, 'rb') as keyf:
|
||||
pubkey = str(keyf.read()).rstrip('\n')
|
||||
if pubkey is None:
|
||||
raise Exception('no pubkey or pk_file found')
|
||||
pubkey = str(pubkey)
|
||||
return base64.b64decode(pubkey)
|
||||
|
||||
|
||||
def keygen(sk_file=None, pk_file=None):
|
||||
'''
|
||||
Use libnacl to generate a keypair.
|
||||
|
||||
If no `sk_file` is defined return a keypair.
|
||||
|
||||
If only the `sk_file` is defined `pk_file` will use the same name with a postfix `.pub`.
|
||||
|
||||
When the `sk_file` is already existing, but `pk_file` is not. The `pk_file` will be generated
|
||||
using the `sk_file`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.keygen
|
||||
salt-run nacl.keygen keyfile=/root/.nacl
|
||||
salt-run --out=newline_values_only nacl.keygen > /root/.nacl
|
||||
salt-call nacl.keygen
|
||||
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
|
||||
salt-call --local nacl.keygen
|
||||
'''
|
||||
b = libnacl.secret.SecretBox()
|
||||
key = b.sk
|
||||
key = base64.b64encode(key)
|
||||
if keyfile:
|
||||
if os.path.isfile(keyfile):
|
||||
raise Exception('file already found: {0}'.format(keyfile))
|
||||
with salt.utils.files.fopen(keyfile, 'w') as keyf:
|
||||
keyf.write(key)
|
||||
return 'saved: {0}'.format(keyfile)
|
||||
return key
|
||||
if sk_file is None:
|
||||
kp = libnacl.public.SecretKey()
|
||||
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
|
||||
|
||||
if pk_file is None:
|
||||
pk_file = '{0}.pub'.format(sk_file)
|
||||
|
||||
if sk_file and pk_file is None:
|
||||
if not os.path.isfile(sk_file):
|
||||
kp = libnacl.public.SecretKey()
|
||||
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.sk))
|
||||
if salt.utils.is_windows():
|
||||
cur_user = salt.utils.win_functions.get_current_user()
|
||||
salt.utils.win_dacl.set_owner(sk_file, cur_user)
|
||||
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
|
||||
else:
|
||||
# chmod 0600 file
|
||||
os.chmod(sk_file, 1536)
|
||||
return 'saved sk_file: {0}'.format(sk_file)
|
||||
else:
|
||||
raise Exception('sk_file:{0} already exist.'.format(sk_file))
|
||||
|
||||
if sk_file is None and pk_file:
|
||||
raise Exception('sk_file: Must be set inorder to generate a public key.')
|
||||
|
||||
if os.path.isfile(sk_file) and os.path.isfile(pk_file):
|
||||
raise Exception('sk_file:{0} and pk_file:{1} already exist.'.format(sk_file, pk_file))
|
||||
|
||||
if os.path.isfile(sk_file) and not os.path.isfile(pk_file):
|
||||
# generate pk using the sk
|
||||
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||
sk = str(keyf.read()).rstrip('\n')
|
||||
sk = base64.b64decode(sk)
|
||||
kp = libnacl.public.SecretKey(sk)
|
||||
with salt.utils.fopen(pk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.pk))
|
||||
return 'saved pk_file: {0}'.format(pk_file)
|
||||
|
||||
kp = libnacl.public.SecretKey()
|
||||
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.sk))
|
||||
if salt.utils.is_windows():
|
||||
cur_user = salt.utils.win_functions.get_current_user()
|
||||
salt.utils.win_dacl.set_owner(sk_file, cur_user)
|
||||
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
|
||||
else:
|
||||
# chmod 0600 file
|
||||
os.chmod(sk_file, 1536)
|
||||
with salt.utils.fopen(pk_file, 'w') as keyf:
|
||||
keyf.write(base64.b64encode(kp.pk))
|
||||
return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file)
|
||||
|
||||
|
||||
def enc(data, **kwargs):
|
||||
'''
|
||||
Takes a key generated from `nacl.keygen` and encrypt some data.
|
||||
Alias to `{box_type}_encrypt`
|
||||
|
||||
box_type: secretbox, sealedbox(default)
|
||||
'''
|
||||
box_type = _get_config(**kwargs)['box_type']
|
||||
if box_type == 'sealedbox':
|
||||
return sealedbox_encrypt(data, **kwargs)
|
||||
if box_type == 'secretbox':
|
||||
return secretbox_encrypt(data, **kwargs)
|
||||
return sealedbox_encrypt(data, **kwargs)
|
||||
|
||||
|
||||
def enc_file(name, out=None, **kwargs):
|
||||
'''
|
||||
This is a helper function to encrypt a file and return its contents.
|
||||
|
||||
You can provide an optional output file using `out`
|
||||
|
||||
`name` can be a local file or when not using `salt-run` can be a url like `salt://`, `https://` etc.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.enc datatoenc
|
||||
salt-run nacl.enc datatoenc keyfile=/root/.nacl
|
||||
salt-run nacl.enc datatoenc key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
||||
salt-run nacl.enc_file name=/tmp/id_rsa
|
||||
salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert
|
||||
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
|
||||
sk_file=/etc/salt/pki/master/nacl.pub
|
||||
'''
|
||||
key = _get_key(**kwargs)
|
||||
sk = base64.b64decode(key)
|
||||
b = libnacl.secret.SecretBox(sk)
|
||||
return base64.b64encode(b.encrypt(data))
|
||||
try:
|
||||
data = __salt__['cp.get_file_str'](name)
|
||||
except Exception as e:
|
||||
# likly using salt-run so fallback to local filesystem
|
||||
with salt.utils.fopen(name, 'rb') as f:
|
||||
data = f.read()
|
||||
d = enc(data, **kwargs)
|
||||
if out:
|
||||
if os.path.isfile(out):
|
||||
raise Exception('file:{0} already exist.'.format(out))
|
||||
with salt.utils.fopen(out, 'wb') as f:
|
||||
f.write(d)
|
||||
return 'Wrote: {0}'.format(out)
|
||||
return d
|
||||
|
||||
|
||||
def dec(data, **kwargs):
|
||||
'''
|
||||
Takes a key generated from `nacl.keygen` and decrypt some data.
|
||||
Alias to `{box_type}_decrypt`
|
||||
|
||||
box_type: secretbox, sealedbox(default)
|
||||
'''
|
||||
box_type = _get_config(**kwargs)['box_type']
|
||||
if box_type == 'sealedbox':
|
||||
return sealedbox_decrypt(data, **kwargs)
|
||||
if box_type == 'secretbox':
|
||||
return secretbox_decrypt(data, **kwargs)
|
||||
return sealedbox_decrypt(data, **kwargs)
|
||||
|
||||
|
||||
def dec_file(name, out=None, **kwargs):
|
||||
'''
|
||||
This is a helper function to decrypt a file and return its contents.
|
||||
|
||||
You can provide an optional output file using `out`
|
||||
|
||||
`name` can be a local file or when not using `salt-run` can be a url like `salt://`, `https://` etc.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.dec pEXHQM6cuaF7A=
|
||||
salt-run nacl.dec data='pEXHQM6cuaF7A=' keyfile=/root/.nacl
|
||||
salt-run nacl.dec data='pEXHQM6cuaF7A=' key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
||||
salt-run nacl.dec_file name=/tmp/id_rsa.nacl
|
||||
salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa
|
||||
salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \
|
||||
sk_file=/etc/salt/pki/master/nacl.pub
|
||||
'''
|
||||
key = _get_key(**kwargs)
|
||||
sk = base64.b64decode(key)
|
||||
b = libnacl.secret.SecretBox(key=sk)
|
||||
try:
|
||||
data = __salt__['cp.get_file_str'](name)
|
||||
except Exception as e:
|
||||
# likly using salt-run so fallback to local filesystem
|
||||
with salt.utils.fopen(name, 'rb') as f:
|
||||
data = f.read()
|
||||
d = dec(data, **kwargs)
|
||||
if out:
|
||||
if os.path.isfile(out):
|
||||
raise Exception('file:{0} already exist.'.format(out))
|
||||
with salt.utils.fopen(out, 'wb') as f:
|
||||
f.write(d)
|
||||
return 'Wrote: {0}'.format(out)
|
||||
return d
|
||||
|
||||
|
||||
def sealedbox_encrypt(data, **kwargs):
|
||||
'''
|
||||
Encrypt data using a public key generated from `nacl.keygen`.
|
||||
The encryptd data can be decrypted using `nacl.sealedbox_decrypt` only with the secret key.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.sealedbox_encrypt datatoenc
|
||||
salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub
|
||||
salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ='
|
||||
'''
|
||||
pk = _get_pk(**kwargs)
|
||||
b = libnacl.sealed.SealedBox(pk)
|
||||
return base64.b64encode(b.encrypt(data))
|
||||
|
||||
|
||||
def sealedbox_decrypt(data, **kwargs):
|
||||
'''
|
||||
Decrypt data using a secret key that was encrypted using a public key with `nacl.sealedbox_encrypt`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.sealedbox_decrypt pEXHQM6cuaF7A=
|
||||
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
|
||||
'''
|
||||
if data is None:
|
||||
return None
|
||||
sk = _get_sk(**kwargs)
|
||||
keypair = libnacl.public.SecretKey(sk)
|
||||
b = libnacl.sealed.SealedBox(keypair)
|
||||
return b.decrypt(base64.b64decode(data))
|
||||
|
||||
|
||||
def secretbox_encrypt(data, **kwargs):
|
||||
'''
|
||||
Encrypt data using a secret key generated from `nacl.keygen`.
|
||||
The same secret key can be used to decrypt the data using `nacl.secretbox_decrypt`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run nacl.secretbox_encrypt datatoenc
|
||||
salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
|
||||
'''
|
||||
sk = _get_sk(**kwargs)
|
||||
b = libnacl.secret.SecretBox(sk)
|
||||
return base64.b64encode(b.encrypt(data))
|
||||
|
||||
|
||||
def secretbox_decrypt(data, **kwargs):
|
||||
'''
|
||||
Decrypt data that was encrypted using `nacl.secretbox_encrypt` using the secret key
|
||||
that was generated from `nacl.keygen`.
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-call nacl.secretbox_decrypt pEXHQM6cuaF7A=
|
||||
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
|
||||
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
|
||||
'''
|
||||
if data is None:
|
||||
return None
|
||||
key = _get_sk(**kwargs)
|
||||
b = libnacl.secret.SecretBox(key=key)
|
||||
return b.decrypt(base64.b64decode(data))
|
||||
|
@ -85,7 +85,7 @@ def _check_for_changes(entity_type, ret, existing, modified):
|
||||
if 'generation' in existing['content'].keys():
|
||||
del existing['content']['generation']
|
||||
|
||||
if cmp(modified['content'], existing['content']) == 0:
|
||||
if modified['content'] == existing['content']:
|
||||
ret['comment'] = '{entity_type} is currently enforced to the desired state. No changes made.'.format(entity_type=entity_type)
|
||||
else:
|
||||
ret['comment'] = '{entity_type} was enforced to the desired state. Note: Only parameters specified ' \
|
||||
@ -94,7 +94,7 @@ def _check_for_changes(entity_type, ret, existing, modified):
|
||||
ret['changes']['new'] = modified['content']
|
||||
|
||||
else:
|
||||
if cmp(modified, existing) == 0:
|
||||
if modified == existing:
|
||||
ret['comment'] = '{entity_type} is currently enforced to the desired state. No changes made.'.format(entity_type=entity_type)
|
||||
else:
|
||||
ret['comment'] = '{entity_type} was enforced to the desired state. Note: Only parameters specified ' \
|
||||
|
@ -37,14 +37,16 @@ Connection module for Amazon Cloud Formation
|
||||
- name: mystack
|
||||
'''
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
import logging
|
||||
import json
|
||||
|
||||
# Import 3rd-party libs
|
||||
# Import Salt libs
|
||||
import salt.utils.compat
|
||||
from salt.ext import six
|
||||
|
||||
# Import 3rd-party libs
|
||||
try:
|
||||
from salt._compat import ElementTree as ET
|
||||
HAS_ELEMENT_TREE = True
|
||||
@ -142,10 +144,14 @@ def present(name, template_body=None, template_url=None, parameters=None, notifi
|
||||
stack_policy_body = _get_template(stack_policy_body, name)
|
||||
stack_policy_during_update_body = _get_template(stack_policy_during_update_body, name)
|
||||
|
||||
for i in [template_body, stack_policy_body, stack_policy_during_update_body]:
|
||||
if isinstance(i, dict):
|
||||
return i
|
||||
|
||||
_valid = _validate(template_body, template_url, region, key, keyid, profile)
|
||||
log.debug('Validate is : {0}.'.format(_valid))
|
||||
if _valid is not True:
|
||||
code, message = _get_error(_valid)
|
||||
code, message = _valid
|
||||
ret['result'] = False
|
||||
ret['comment'] = 'Template could not be validated.\n{0} \n{1}'.format(code, message)
|
||||
return ret
|
||||
@ -155,7 +161,7 @@ def present(name, template_body=None, template_url=None, parameters=None, notifi
|
||||
template = template['GetTemplateResponse']['GetTemplateResult']['TemplateBody'].encode('ascii', 'ignore')
|
||||
template = json.loads(template)
|
||||
_template_body = json.loads(template_body)
|
||||
compare = cmp(template, _template_body)
|
||||
compare = salt.utils.compat.cmp(template, _template_body)
|
||||
if compare != 0:
|
||||
log.debug('Templates are not the same. Compare value is {0}'.format(compare))
|
||||
# At this point we should be able to run update safely since we already validated the template
|
||||
@ -251,7 +257,7 @@ def _get_template(template, name):
|
||||
def _validate(template_body=None, template_url=None, region=None, key=None, keyid=None, profile=None):
|
||||
# Validates template. returns true if template syntax is correct.
|
||||
validate = __salt__['boto_cfn.validate_template'](template_body, template_url, region, key, keyid, profile)
|
||||
log.debug('Validate is result is {0}.'.format(str(validate)))
|
||||
log.debug('Validate result is {0}.'.format(str(validate)))
|
||||
if isinstance(validate, six.string_types):
|
||||
code, message = _get_error(validate)
|
||||
log.debug('Validate error is {0} and message is {1}.'.format(code, message))
|
||||
|
@ -274,7 +274,7 @@ def user_absent(name, delete_keys=True, delete_mfa_devices=True, delete_profile=
|
||||
if profile_deleted:
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} login profile is deleted.'.format(name)])
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} policies are set to be deleted.'.format(name)])
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} managed policies are set to be detached.'.format(name)])
|
||||
ret['result'] = None
|
||||
else:
|
||||
_ret = _user_policies_detached(name, region, key, keyid, profile)
|
||||
@ -283,6 +283,16 @@ def user_absent(name, delete_keys=True, delete_mfa_devices=True, delete_profile=
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} inline policies are set to be deleted.'.format(name)])
|
||||
ret['result'] = None
|
||||
else:
|
||||
_ret = _user_policies_deleted(name, region, key, keyid, profile)
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if not _ret['result']:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
# finally, actually delete the user
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} is set to be deleted.'.format(name)])
|
||||
@ -738,7 +748,49 @@ def _user_policies_detached(
|
||||
newpolicies = [x.get('policy_arn') for x in _list]
|
||||
ret['changes']['new'] = {'managed_policies': newpolicies}
|
||||
msg = '{0} policies detached from user {1}.'
|
||||
ret['comment'] = msg.format(', '.join(newpolicies), name)
|
||||
ret['comment'] = msg.format(', '.join(oldpolicies), name)
|
||||
return ret
|
||||
|
||||
|
||||
def _user_policies_deleted(
|
||||
name,
|
||||
region=None,
|
||||
key=None,
|
||||
keyid=None,
|
||||
profile=None):
|
||||
ret = {'result': True, 'comment': '', 'changes': {}}
|
||||
oldpolicies = __salt__['boto_iam.get_all_user_policies'](user_name=name,
|
||||
region=region, key=key, keyid=keyid, profile=profile)
|
||||
if not oldpolicies:
|
||||
msg = 'No inline policies in user {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
if __opts__['test']:
|
||||
msg = '{0} policies to be deleted from user {1}.'
|
||||
ret['comment'] = msg.format(', '.join(oldpolicies), name)
|
||||
ret['result'] = None
|
||||
return ret
|
||||
ret['changes']['old'] = {'inline_policies': oldpolicies}
|
||||
for policy_name in oldpolicies:
|
||||
policy_deleted = __salt__['boto_iam.delete_user_policy'](name,
|
||||
policy_name,
|
||||
region=region, key=key,
|
||||
keyid=keyid,
|
||||
profile=profile)
|
||||
if not policy_deleted:
|
||||
newpolicies = __salt__['boto_iam.get_all_user_policies'](name, region=region,
|
||||
key=key, keyid=keyid,
|
||||
profile=profile)
|
||||
ret['changes']['new'] = {'inline_policies': newpolicies}
|
||||
ret['result'] = False
|
||||
msg = 'Failed to detach {0} from user {1}'
|
||||
ret['comment'] = msg.format(policy_name, name)
|
||||
return ret
|
||||
newpolicies = __salt__['boto_iam.get_all_user_policies'](name, region=region, key=key,
|
||||
keyid=keyid, profile=profile)
|
||||
ret['changes']['new'] = {'inline_policies': newpolicies}
|
||||
msg = '{0} policies deleted from user {1}.'
|
||||
ret['comment'] = msg.format(', '.join(oldpolicies), name)
|
||||
return ret
|
||||
|
||||
|
||||
@ -790,7 +842,7 @@ def group_absent(name, region=None, key=None, keyid=None, profile=None):
|
||||
ret['comment'] = 'IAM Group {0} does not exist.'.format(name)
|
||||
return ret
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} policies are set to be deleted.'.format(name)])
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} managed policies are set to be detached.'.format(name)])
|
||||
ret['result'] = None
|
||||
else:
|
||||
_ret = _group_policies_detached(name, region, key, keyid, profile)
|
||||
@ -799,6 +851,16 @@ def group_absent(name, region=None, key=None, keyid=None, profile=None):
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
if __opts__['test']:
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} inline policies are set to be deleted.'.format(name)])
|
||||
ret['result'] = None
|
||||
else:
|
||||
_ret = _group_policies_deleted(name, region, key, keyid, profile)
|
||||
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
|
||||
if not _ret['result']:
|
||||
ret['result'] = _ret['result']
|
||||
if ret['result'] is False:
|
||||
return ret
|
||||
ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} users are set to be removed.'.format(name)])
|
||||
existing_users = __salt__['boto_iam.get_group_members'](group_name=name, region=region, key=key, keyid=keyid, profile=profile)
|
||||
_ret = _case_group(ret, [], name, existing_users, region, key, keyid, profile)
|
||||
@ -1152,6 +1214,48 @@ def _group_policies_detached(
|
||||
return ret
|
||||
|
||||
|
||||
def _group_policies_deleted(
|
||||
name,
|
||||
region=None,
|
||||
key=None,
|
||||
keyid=None,
|
||||
profile=None):
|
||||
ret = {'result': True, 'comment': '', 'changes': {}}
|
||||
oldpolicies = __salt__['boto_iam.get_all_group_policies'](group_name=name,
|
||||
region=region, key=key, keyid=keyid, profile=profile)
|
||||
if not oldpolicies:
|
||||
msg = 'No inline policies in group {0}.'.format(name)
|
||||
ret['comment'] = msg
|
||||
return ret
|
||||
if __opts__['test']:
|
||||
msg = '{0} policies to be deleted from group {1}.'
|
||||
ret['comment'] = msg.format(', '.join(oldpolicies), name)
|
||||
ret['result'] = None
|
||||
return ret
|
||||
ret['changes']['old'] = {'inline_policies': oldpolicies}
|
||||
for policy_name in oldpolicies:
|
||||
policy_deleted = __salt__['boto_iam.delete_group_policy'](name,
|
||||
policy_name,
|
||||
region=region, key=key,
|
||||
keyid=keyid,
|
||||
profile=profile)
|
||||
if not policy_deleted:
|
||||
newpolicies = __salt__['boto_iam.get_all_group_policies'](name, region=region,
|
||||
key=key, keyid=keyid,
|
||||
profile=profile)
|
||||
ret['changes']['new'] = {'inline_policies': newpolicies}
|
||||
ret['result'] = False
|
||||
msg = 'Failed to detach {0} from group {1}'
|
||||
ret['comment'] = msg.format(policy_name, name)
|
||||
return ret
|
||||
newpolicies = __salt__['boto_iam.get_all_group_policies'](name, region=region, key=key,
|
||||
keyid=keyid, profile=profile)
|
||||
ret['changes']['new'] = {'inline_policies': newpolicies}
|
||||
msg = '{0} policies deleted from group {1}.'
|
||||
ret['comment'] = msg.format(', '.join(oldpolicies), name)
|
||||
return ret
|
||||
|
||||
|
||||
def account_policy(name=None, allow_users_to_change_password=None,
|
||||
hard_expiry=None, max_password_age=None,
|
||||
minimum_password_length=None, password_reuse_prevention=None,
|
||||
|
@ -8,6 +8,12 @@ For documentation on setting up the cisconso proxy minion look in the documentat
|
||||
for :mod:`salt.proxy.cisconso <salt.proxy.cisconso>`.
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt libs
|
||||
import salt.utils.compat
|
||||
|
||||
|
||||
def __virtual__():
|
||||
return 'cisconso.set_data_value' in __salt__
|
||||
@ -53,7 +59,7 @@ def value_present(name, datastore, path, config):
|
||||
|
||||
existing = __salt__['cisconso.get_data'](datastore, path)
|
||||
|
||||
if cmp(existing, config):
|
||||
if salt.utils.compat.cmp(existing, config):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Config is already set'
|
||||
|
||||
|
@ -146,7 +146,7 @@ def running(name,
|
||||
.. _docker-container-running-skip-translate:
|
||||
|
||||
skip_translate
|
||||
This function translates Salt CLI input into the format which
|
||||
This function translates Salt CLI or SLS input into the format which
|
||||
docker-py_ expects. However, in the event that Salt's translation logic
|
||||
fails (due to potential changes in the Docker Remote API, or to bugs in
|
||||
the translation code), this argument can be used to exert granular
|
||||
@ -678,24 +678,14 @@ def running(name,
|
||||
- foo2.domain.tld
|
||||
|
||||
domainname
|
||||
Set custom DNS search domains. Can be expressed as a comma-separated
|
||||
list or a YAML list. The below two examples are equivalent:
|
||||
The domain name to use for the container
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
foo:
|
||||
docker_container.running:
|
||||
- image: bar/baz:latest
|
||||
- dommainname: domain.tld,domain2.tld
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
foo:
|
||||
docker_container.running:
|
||||
- image: bar/baz:latest
|
||||
- dommainname:
|
||||
- domain.tld
|
||||
- domain2.tld
|
||||
- dommainname: domain.tld
|
||||
|
||||
entrypoint
|
||||
Entrypoint for the container
|
||||
|
@ -1437,7 +1437,7 @@ def absent(name):
|
||||
ret['comment'] = 'File {0} is set for removal'.format(name)
|
||||
return ret
|
||||
try:
|
||||
if salt.utils.is_windows():
|
||||
if salt.utils.platform.is_windows():
|
||||
__salt__['file.remove'](name, force=True)
|
||||
else:
|
||||
__salt__['file.remove'](name)
|
||||
@ -1454,7 +1454,7 @@ def absent(name):
|
||||
ret['comment'] = 'Directory {0} is set for removal'.format(name)
|
||||
return ret
|
||||
try:
|
||||
if salt.utils.is_windows():
|
||||
if salt.utils.platform.is_windows():
|
||||
__salt__['file.remove'](name, force=True)
|
||||
else:
|
||||
__salt__['file.remove'](name)
|
||||
|
@ -27,7 +27,7 @@ from salt.states.git import _fail, _neutral_test
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HG_BINARY = 'hg.exe' if salt.utils.is_windows() else 'hg'
|
||||
HG_BINARY = 'hg.exe' if salt.utils.platform.is_windows() else 'hg'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
|
@ -206,7 +206,7 @@ def sections_present(name, sections=None, separator='='):
|
||||
ret['result'] = False
|
||||
ret['comment'] = "{0}".format(err)
|
||||
return ret
|
||||
if cmp(dict(sections[section]), cur_section) == 0:
|
||||
if dict(sections[section]) == cur_section:
|
||||
ret['comment'] += 'Section unchanged {0}.\n'.format(section)
|
||||
continue
|
||||
elif cur_section:
|
||||
|
@ -178,15 +178,12 @@ def _fulfills_version_spec(versions, oper, desired_version,
|
||||
if isinstance(versions, dict) and 'version' in versions:
|
||||
versions = versions['version']
|
||||
for ver in versions:
|
||||
if oper == '==':
|
||||
if fnmatch.fnmatch(ver, desired_version):
|
||||
return True
|
||||
|
||||
elif salt.utils.compare_versions(ver1=ver,
|
||||
oper=oper,
|
||||
ver2=desired_version,
|
||||
cmp_func=cmp_func,
|
||||
ignore_epoch=ignore_epoch):
|
||||
if (oper == '==' and fnmatch.fnmatch(ver, desired_version)) \
|
||||
or salt.utils.compare_versions(ver1=ver,
|
||||
oper=oper,
|
||||
ver2=desired_version,
|
||||
cmp_func=cmp_func,
|
||||
ignore_epoch=ignore_epoch):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -84,7 +84,7 @@ def present(name, **kwargs):
|
||||
for right in kwargs['rights']:
|
||||
for key in right:
|
||||
right[key] = str(right[key])
|
||||
if cmp(sorted(kwargs['rights']), sorted(usergroup['rights'])) != 0:
|
||||
if sorted(kwargs['rights']) != sorted(usergroup['rights']):
|
||||
update_rights = True
|
||||
else:
|
||||
update_rights = True
|
||||
|
@ -47,6 +47,7 @@ import salt.config
|
||||
import salt.loader
|
||||
import salt.template
|
||||
import salt.utils # Can be removed when pem_finger is moved
|
||||
import salt.utils.compat
|
||||
import salt.utils.event
|
||||
import salt.utils.files
|
||||
import salt.utils.platform
|
||||
@ -3055,7 +3056,7 @@ def diff_node_cache(prov_dir, node, new_data, opts):
|
||||
# Perform a simple diff between the old and the new data, and if it differs,
|
||||
# return both dicts.
|
||||
# TODO: Return an actual diff
|
||||
diff = cmp(new_data, cache_data)
|
||||
diff = salt.utils.compat.cmp(new_data, cache_data)
|
||||
if diff != 0:
|
||||
fire_event(
|
||||
'event',
|
||||
|
@ -46,3 +46,15 @@ def deepcopy_bound(name):
|
||||
finally:
|
||||
copy._deepcopy_dispatch = pre_dispatch
|
||||
return ret
|
||||
|
||||
|
||||
def cmp(x, y):
|
||||
'''
|
||||
Compatibility helper function to replace the ``cmp`` function from Python 2. The
|
||||
``cmp`` function is no longer available in Python 3.
|
||||
|
||||
cmp(x, y) -> integer
|
||||
|
||||
Return negative if x<y, zero if x==y, positive if x>y.
|
||||
'''
|
||||
return (x > y) - (x < y)
|
||||
|
@ -188,9 +188,8 @@ def memoize(func):
|
||||
str_args.append(str(arg))
|
||||
else:
|
||||
str_args.append(arg)
|
||||
args = str_args
|
||||
|
||||
args_ = ','.join(list(args) + ['{0}={1}'.format(k, kwargs[k]) for k in sorted(kwargs)])
|
||||
args_ = ','.join(list(str_args) + ['{0}={1}'.format(k, kwargs[k]) for k in sorted(kwargs)])
|
||||
if args_ not in cache:
|
||||
cache[args_] = func(*args, **kwargs)
|
||||
return cache[args_]
|
||||
|
@ -387,7 +387,7 @@ def dns(val, **kwargs):
|
||||
|
||||
|
||||
def domainname(val, **kwargs): # pylint: disable=unused-argument
|
||||
return _translate_stringlist(val)
|
||||
return _translate_str(val)
|
||||
|
||||
|
||||
def entrypoint(val, **kwargs): # pylint: disable=unused-argument
|
||||
|
@ -777,7 +777,8 @@ def _render(template, render, renderer, template_dict, opts):
|
||||
blacklist = opts.get('renderer_blacklist')
|
||||
whitelist = opts.get('renderer_whitelist')
|
||||
ret = compile_template(template, rend, renderer, blacklist, whitelist, **template_dict)
|
||||
ret = ret.read()
|
||||
if salt.utils.stringio.is_readable(ret):
|
||||
ret = ret.read()
|
||||
if str(ret).startswith('#!') and not str(ret).startswith('#!/'):
|
||||
ret = str(ret).split('\n', 1)[1]
|
||||
return ret
|
||||
|
@ -274,6 +274,8 @@ class ReactWrap(object):
|
||||
try:
|
||||
f_call = salt.utils.format_call(l_fun, low)
|
||||
kwargs = f_call.get('kwargs', {})
|
||||
if 'arg' not in kwargs:
|
||||
kwargs['arg'] = []
|
||||
if 'kwarg' not in kwargs:
|
||||
kwargs['kwarg'] = {}
|
||||
|
||||
|
@ -726,6 +726,43 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin):
|
||||
ret = self.run_state('pkg.removed', name=target)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
def test_pkg_014_installed_missing_release(self, grains=None): # pylint: disable=unused-argument
|
||||
'''
|
||||
Tests that a version number missing the release portion still resolves
|
||||
as correctly installed. For example, version 2.0.2 instead of 2.0.2-1.el7
|
||||
'''
|
||||
os_family = grains.get('os_family', '')
|
||||
|
||||
if os_family.lower() != 'redhat':
|
||||
self.skipTest('Test only runs on RedHat OS family')
|
||||
|
||||
pkg_targets = _PKG_TARGETS.get(os_family, [])
|
||||
|
||||
# Make sure that we have targets that match the os_family. If this
|
||||
# fails then the _PKG_TARGETS dict above needs to have an entry added,
|
||||
# with two packages that are not installed before these tests are run
|
||||
self.assertTrue(pkg_targets)
|
||||
|
||||
target = pkg_targets[0]
|
||||
version = self.run_function('pkg.version', [target])
|
||||
|
||||
# If this assert fails, we need to find new targets, this test needs to
|
||||
# be able to test successful installation of packages, so this package
|
||||
# needs to not be installed before we run the states below
|
||||
self.assertFalse(version)
|
||||
|
||||
ret = self.run_state(
|
||||
'pkg.installed',
|
||||
name=target,
|
||||
version=salt.utils.str_version_to_evr(version)[1],
|
||||
refresh=False,
|
||||
)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
# Clean up
|
||||
ret = self.run_state('pkg.removed', name=target)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
@requires_salt_modules('pkg.group_install')
|
||||
@requires_system_grains
|
||||
def test_group_installed_handle_missing_package_group(self, grains=None): # pylint: disable=unused-argument
|
||||
|
@ -821,7 +821,7 @@ class TranslateInputTestCase(TestCase):
|
||||
expected
|
||||
)
|
||||
|
||||
@assert_stringlist
|
||||
@assert_string
|
||||
def test_domainname(self):
|
||||
'''
|
||||
Should be a list of strings or converted to one
|
||||
|
Loading…
Reference in New Issue
Block a user