mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
nacl updates
This commit is contained in:
parent
fa8d672f9a
commit
2b79ba2f32
3 changed files with 725 additions and 166 deletions
|
@ -5,3 +5,4 @@ yappi>=0.8.2
|
||||||
--allow-unverified python-neutronclient>2.3.6
|
--allow-unverified python-neutronclient>2.3.6
|
||||||
python-gnupg
|
python-gnupg
|
||||||
cherrypy>=3.2.2
|
cherrypy>=3.2.2
|
||||||
|
libnacl
|
||||||
|
|
|
@ -11,112 +11,157 @@ regardless if they are encrypted or not.
|
||||||
When generating keys and encrypting passwords use --local when using salt-call for extra
|
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.
|
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.
|
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
|
.. 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
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-call --local nacl.enc mypass keyfile=/root/.nacl
|
salt-call --local nacl.enc asecretpass pk=/kfGX7PbWeu099702PBbKWLpG/9p06IQRswkdWHCDk0=
|
||||||
DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=
|
tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=
|
||||||
|
|
||||||
To decrypt the data:
|
To decrypt the data:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-call --local nacl.dec data='DRB7Q6/X5gGSRCTpZyxS6hXO5LnlJIIJ4ivbmUlbWj0llUA+uaVyvou3vJ4=' keyfile=/root/.nacl
|
salt-call --local nacl.dec data='tqXzeIJnTAM9Xf0mdLcpEdklMbfBGPj2oTKmlgrm3S1DTVVHNnh9h8mU1GKllGq/+cYsk6m5WhGdk58=' \
|
||||||
mypass
|
sk='SVWut5SqNpuPeNzb1b9y6b2eXg2PLIog43GBzp48Sow='
|
||||||
|
|
||||||
The following optional configurations can be defined in the
|
When the keys are defined in the master config you can use them from the nacl runner
|
||||||
minion or master config. Avoid storing the config in pillars!
|
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:
|
nacl.config:
|
||||||
key: 'cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
sk_file: /etc/salt/pki/master/nacl
|
||||||
keyfile: /root/.nacl
|
pk: 'cTIqXwnUiD1ulg4kXsbeCE7/NoeKEzd4nLeYcCFpd9k='
|
||||||
|
|
||||||
When the key is defined in the master config you can use it from the nacl runner:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: jinja
|
||||||
|
|
||||||
pillarexample:
|
pillarexample:
|
||||||
user: root
|
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:
|
Larger files like certificates can be encrypted with:
|
||||||
|
|
||||||
.. 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:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
In Pillars rended with jinja be sure to include `|json` so line breaks are encoded:
|
||||||
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:
|
|
||||||
|
|
||||||
.. code-block:: jinja
|
.. code-block:: jinja
|
||||||
|
|
||||||
secret.txt:
|
cert: "{{salt.nacl.dec('S2uogToXkgENz9...085KYt')|json}}"
|
||||||
file.managed:
|
|
||||||
- template: jinja
|
In States rendered with jinja it is also good pratice to include `|json`:
|
||||||
- user: user
|
|
||||||
- group: group
|
.. code-block:: jinja
|
||||||
- mode: 700
|
|
||||||
- contents: "{{- salt['pillar.get']('secret') }}"
|
{{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
|
from __future__ import absolute_import
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
import salt.utils.files
|
import salt.utils
|
||||||
import salt.syspaths
|
import salt.syspaths
|
||||||
|
|
||||||
|
|
||||||
REQ_ERROR = None
|
REQ_ERROR = None
|
||||||
try:
|
try:
|
||||||
import libnacl.secret
|
import libnacl.secret
|
||||||
|
import libnacl.sealed
|
||||||
except (ImportError, OSError) as e:
|
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'
|
__virtualname__ = 'nacl'
|
||||||
|
|
||||||
|
@ -130,91 +175,284 @@ def _get_config(**kwargs):
|
||||||
Return configuration
|
Return configuration
|
||||||
'''
|
'''
|
||||||
config = {
|
config = {
|
||||||
'key': None,
|
'box_type': 'sealedbox',
|
||||||
'keyfile': None,
|
'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_key = '{0}.config'.format(__virtualname__)
|
||||||
config.update(__salt__['config.get'](config_key, {}))
|
try:
|
||||||
for k in set(config) & set(kwargs):
|
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]
|
config[k] = kwargs[k]
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def _get_key(rstrip_newline=True, **kwargs):
|
def _get_sk(**kwargs):
|
||||||
'''
|
'''
|
||||||
Return key
|
Return sk
|
||||||
'''
|
'''
|
||||||
config = _get_config(**kwargs)
|
config = _get_config(**kwargs)
|
||||||
key = config['key']
|
key = config['sk']
|
||||||
keyfile = config['keyfile']
|
sk_file = config['sk_file']
|
||||||
if not key and keyfile:
|
if not key and sk_file:
|
||||||
if not os.path.isfile(keyfile):
|
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||||
raise Exception('file not found: {0}'.format(keyfile))
|
key = str(keyf.read()).rstrip('\n')
|
||||||
with salt.utils.files.fopen(keyfile, 'rb') as keyf:
|
|
||||||
key = keyf.read()
|
|
||||||
if key is None:
|
if key is None:
|
||||||
raise Exception('no key found')
|
raise Exception('no key or sk_file found')
|
||||||
key = str(key)
|
return base64.b64decode(key)
|
||||||
if rstrip_newline:
|
|
||||||
key = key.rstrip('\n')
|
|
||||||
return 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:
|
CLI Examples:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
||||||
salt-call --local nacl.keygen keyfile=/root/.nacl
|
|
||||||
salt-call --local --out=newline_values_only nacl.keygen > /root/.nacl
|
|
||||||
'''
|
'''
|
||||||
b = libnacl.secret.SecretBox()
|
if sk_file is None:
|
||||||
key = b.sk
|
kp = libnacl.public.SecretKey()
|
||||||
key = base64.b64encode(key)
|
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
|
||||||
if keyfile:
|
|
||||||
if os.path.isfile(keyfile):
|
if pk_file is None:
|
||||||
raise Exception('file already found: {0}'.format(keyfile))
|
pk_file = '{0}.pub'.format(sk_file)
|
||||||
with salt.utils.files.fopen(keyfile, 'w') as keyf:
|
|
||||||
keyf.write(key)
|
if sk_file and pk_file is None:
|
||||||
return 'saved: {0}'.format(keyfile)
|
if not os.path.isfile(sk_file):
|
||||||
return key
|
kp = libnacl.public.SecretKey()
|
||||||
|
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||||
|
keyf.write(base64.b64encode(kp.sk))
|
||||||
|
if not salt.utils.is_windows():
|
||||||
|
os.chmod(sk_file, 1536) # 0600
|
||||||
|
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 not salt.utils.is_windows():
|
||||||
|
os.chmod(sk_file, 1536) # 0600
|
||||||
|
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):
|
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:
|
CLI Examples:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-call --local nacl.enc datatoenc
|
salt-run nacl.enc_file name=/tmp/id_rsa
|
||||||
salt-call --local nacl.enc datatoenc keyfile=/root/.nacl
|
salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert
|
||||||
salt-call --local nacl.enc datatoenc key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
|
||||||
|
sk_file=/etc/salt/pki/master/nacl.pub
|
||||||
'''
|
'''
|
||||||
key = _get_key(**kwargs)
|
try:
|
||||||
sk = base64.b64decode(key)
|
data = __salt__['cp.get_file_str'](name)
|
||||||
b = libnacl.secret.SecretBox(sk)
|
except Exception as e:
|
||||||
return base64.b64encode(b.encrypt(data))
|
# 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):
|
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:
|
CLI Examples:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-call --local nacl.dec pEXHQM6cuaF7A=
|
salt-run nacl.dec_file name=/tmp/id_rsa.nacl
|
||||||
salt-call --local nacl.dec data='pEXHQM6cuaF7A=' keyfile=/root/.nacl
|
salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa
|
||||||
salt-call --local nacl.dec data='pEXHQM6cuaF7A=' key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
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)
|
try:
|
||||||
sk = base64.b64decode(key)
|
data = __salt__['cp.get_file_str'](name)
|
||||||
b = libnacl.secret.SecretBox(key=sk)
|
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))
|
return b.decrypt(base64.b64decode(data))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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
|
:depends: libnacl, https://github.com/saltstack/libnacl
|
||||||
|
|
||||||
|
@ -8,35 +8,160 @@ 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
|
share your pillar data with others that you trust. I don't advise making your pillars public
|
||||||
regardless if they are encrypted or not.
|
regardless if they are encrypted or not.
|
||||||
|
|
||||||
The following configurations can be defined in the master config
|
When generating keys and encrypting passwords use --local when using salt-call for extra
|
||||||
so your users can create encrypted passwords using the runner nacl:
|
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
|
.. 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:
|
nacl.config:
|
||||||
key: 'cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
sk_file: /etc/salt/pki/master/nacl
|
||||||
keyfile: /root/.nacl
|
pk: 'cTIqXwnUiD1ulg4kXsbeCE7/NoeKEzd4nLeYcCFpd9k='
|
||||||
|
|
||||||
Now with the config in the master you can use the runner nacl like:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
from __future__ import absolute_import
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
import salt.utils.files
|
import salt.utils
|
||||||
import salt.syspaths
|
import salt.syspaths
|
||||||
|
|
||||||
|
|
||||||
REQ_ERROR = None
|
REQ_ERROR = None
|
||||||
try:
|
try:
|
||||||
import libnacl.secret
|
import libnacl.secret
|
||||||
except ImportError as e:
|
import libnacl.sealed
|
||||||
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package'
|
except (ImportError, OSError) as e:
|
||||||
|
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.'
|
||||||
|
|
||||||
__virtualname__ = 'nacl'
|
__virtualname__ = 'nacl'
|
||||||
|
|
||||||
|
@ -50,91 +175,286 @@ def _get_config(**kwargs):
|
||||||
Return configuration
|
Return configuration
|
||||||
'''
|
'''
|
||||||
config = {
|
config = {
|
||||||
'key': None,
|
'box_type': 'sealedbox',
|
||||||
'keyfile': None,
|
'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_key = '{0}.config'.format(__virtualname__)
|
||||||
config.update(__opts__.get(config_key, {}))
|
try:
|
||||||
for k in set(config) & set(kwargs):
|
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]
|
config[k] = kwargs[k]
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
def _get_key(rstrip_newline=True, **kwargs):
|
def _get_sk(**kwargs):
|
||||||
'''
|
'''
|
||||||
Return key
|
Return sk
|
||||||
'''
|
'''
|
||||||
config = _get_config(**kwargs)
|
config = _get_config(**kwargs)
|
||||||
key = config['key']
|
key = config['sk']
|
||||||
keyfile = config['keyfile']
|
sk_file = config['sk_file']
|
||||||
if not key and keyfile:
|
if not key and sk_file:
|
||||||
if not os.path.isfile(keyfile):
|
with salt.utils.fopen(sk_file, 'rb') as keyf:
|
||||||
raise Exception('file not found: {0}'.format(keyfile))
|
key = str(keyf.read()).rstrip('\n')
|
||||||
with salt.utils.files.fopen(keyfile, 'rb') as keyf:
|
|
||||||
key = keyf.read()
|
|
||||||
if key is None:
|
if key is None:
|
||||||
raise Exception('no key found')
|
raise Exception('no key or sk_file found')
|
||||||
key = str(key)
|
return base64.b64decode(key)
|
||||||
if rstrip_newline:
|
|
||||||
key = key.rstrip('\n')
|
|
||||||
return 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:
|
CLI Examples:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-run nacl.keygen
|
salt-call nacl.keygen
|
||||||
salt-run nacl.keygen keyfile=/root/.nacl
|
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl
|
||||||
salt-run --out=newline_values_only nacl.keygen > /root/.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()
|
if sk_file is None:
|
||||||
key = b.sk
|
kp = libnacl.public.SecretKey()
|
||||||
key = base64.b64encode(key)
|
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
|
||||||
if keyfile:
|
|
||||||
if os.path.isfile(keyfile):
|
if pk_file is None:
|
||||||
raise Exception('file already found: {0}'.format(keyfile))
|
pk_file = '{0}.pub'.format(sk_file)
|
||||||
with salt.utils.files.fopen(keyfile, 'w') as keyf:
|
|
||||||
keyf.write(key)
|
if sk_file and pk_file is None:
|
||||||
return 'saved: {0}'.format(keyfile)
|
if not os.path.isfile(sk_file):
|
||||||
return key
|
kp = libnacl.public.SecretKey()
|
||||||
|
with salt.utils.fopen(sk_file, 'w') as keyf:
|
||||||
|
keyf.write(base64.b64encode(kp.sk))
|
||||||
|
if not salt.utils.is_windows():
|
||||||
|
# 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 not salt.utils.is_windows():
|
||||||
|
# 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):
|
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:
|
CLI Examples:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-run nacl.enc datatoenc
|
salt-run nacl.enc_file name=/tmp/id_rsa
|
||||||
salt-run nacl.enc datatoenc keyfile=/root/.nacl
|
salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert
|
||||||
salt-run nacl.enc datatoenc key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
|
||||||
|
sk_file=/etc/salt/pki/master/nacl.pub
|
||||||
'''
|
'''
|
||||||
key = _get_key(**kwargs)
|
try:
|
||||||
sk = base64.b64decode(key)
|
data = __salt__['cp.get_file_str'](name)
|
||||||
b = libnacl.secret.SecretBox(sk)
|
except Exception as e:
|
||||||
return base64.b64encode(b.encrypt(data))
|
# 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):
|
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:
|
CLI Examples:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
salt-run nacl.dec pEXHQM6cuaF7A=
|
salt-run nacl.dec_file name=/tmp/id_rsa.nacl
|
||||||
salt-run nacl.dec data='pEXHQM6cuaF7A=' keyfile=/root/.nacl
|
salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa
|
||||||
salt-run nacl.dec data='pEXHQM6cuaF7A=' key='cKEzd4kXsbeCE7/nLTIqXwnUiD1ulg4NoeeYcCFpd9k='
|
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)
|
try:
|
||||||
sk = base64.b64decode(key)
|
data = __salt__['cp.get_file_str'](name)
|
||||||
b = libnacl.secret.SecretBox(key=sk)
|
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))
|
return b.decrypt(base64.b64decode(data))
|
||||||
|
|
Loading…
Add table
Reference in a new issue