Skip to content

Instantly share code, notes, and snippets.

@mgwilliams
Created December 13, 2013 23:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mgwilliams/22d88f51e4faf42452ac to your computer and use it in GitHub Desktop.
Save mgwilliams/22d88f51e4faf42452ac to your computer and use it in GitHub Desktop.
'''
GPG Runner
'''
# Import Python libs
import re
# Import third party libs
import gnupg
# Import Salt libs
import salt.client
uid_re = re.compile('.*\<(.*)\>')
class GPG(gnupg.GPG):
def sign_key(self, uid):
args = ['--batch', '--yes', '--sign-key', uid]
result = self.result_map['generate'](self)
f = gnupg._make_binary_stream('', self.encoding)
r = self._handle_io(args, f, result)
return r
def _gpg():
return GPG(gnupghome=__opts__.get('gpg_dir', '/etc/salt/gpg/master/'))
def _public_key():
'''
Export the master's public key
'''
return _gpg().export_keys('__salt-master__') or None
def generate_key(length=2048):
'''
Generate the master's key, if it does not exist.
Returns the public key or None if it already exists.
Returns False if key generation fails.
.. code-block:: bash
salt-run gpg.generate_key [length=key_length]
length
Length of the key. Default: 2048.
**Examples:**
Generate a key with the default length of 2048:
.. code-block:: bash
salt-run gpg.generate_key
Generate a longer key:
.. code-block:: bash
salt-run gpg.generate_key length=4096
'''
if _public_key() is not None:
print 'Skipping key generation, already exists.'
return None
print 'Generating keys for master.'
gpg = _gpg()
r = gpg.gen_key(gpg.gen_key_input(name_real='__salt-master__',
name_email='__salt-master__', key_type='RSA', key_length=length))
return gpg.export_keys(r.fingerprint) or False
def public_key(generate=False, length=2048):
'''
Return the master's public key.
.. code-block:: bash
salt-run gpg.public_key [[generate=(true|false)] length=key_length]]
generate
Generate the master's key if it does not already exist.
length
Length of the key, if it needs to be generated.
**Examples:**
Get the public key, generating it with the default length of 2048, if needed:
.. code-block:: bash
salt-run gpg.public_key generate=true
'''
pub = _public_key()
if pub is None and generate:
pub = generate_key(length)
if not pub:
print 'No master key found. Try gpg.generate_key.'
else:
print pub
def import_keys(data):
'''
Import the keys contained in data into the master's keychain.
.. code-block:: bash
salt-run gpg.import_keys \\
"-----BEGIN PGP PUBLIC KEY BLOCK-----\\nVersion: (...) \\n-----END PGP PUBLIC KEY BLOCK-----\\n"
'''
data = data.replace("\\n", "\n")
r = _gpg().import_keys(data)
return r.count or False
def list_minions():
'''
List the minions for which this master has keys.
.. code-block:: bash
salt-run gpg.list_minions
'''
uids = [k['uids'][0] for k in _gpg().list_keys() if '__salt-master__' not in k['uids'][0]]
minions = [uid_re.findall(k)[0] for k in uids]
print 'minions:'
for m in minions:
print ' {0}'.format(m)
def distribute_keys(target='* ', match='glob'):
'''
Distribute the master's public key to minions and import the minion's keys
into the master's keychain.
.. code-block:: bash
salt-run gpg.distribute_keys [target=target_expr [match=expr_form]]
target
A target expression. Default: '*'.
match
The expression form of 'target':
glob - Bash glob completion - Default
pcre - Perl style regular expression
list - Python list of hosts
grain - Match based on a grain comparison
grain_pcre - Grain comparison with a regex
pillar - Pillar data comparison
nodegroup - Match on nodegroup
range - Use a Range server for matching
compound - Pass a compound match string
**Example:**
Distribute keys between the master and the 'webserver' nodegroup:
.. code-block:: bash
salt-run gpg.distribute_keys target=webserver match-nodegroup
'''
master_pub = _public_key()
client = salt.client.LocalClient(__opts__['conf_file'])
gpg = _gpg()
print 'Gathering minions\' public keys (this could take awhile).'
minion_keys = client.cmd(target, 'gpg.public_key', kwarg={'generate': True}, timeout=1200, expr_form=match)
print 'Importing minion keys:'
for minion, key in minion_keys.items():
if import_keys(key):
gpg.sign_key(minion)
print ' {0}: ok'.format(minion)
else:
print ' {0}: fail'.format(minion)
results = client.cmd(target, 'gpg.import_keys', [master_pub], timeout=90, expr_form=match)
print 'Distributing master key:'
for minion, count in results.items():
if count:
print ' {0}: ok'.format(minion)
else:
print ' {0}: fail'.format(minion)
def encrypt(grain_id, data, target='*', match='glob'):
gpg = _gpg()
client = salt.client.LocalClient(__opts__['conf_file'])
minions = client.cmd(target, 'test.ping', timeout=30, expr_form=match).keys()
for minion in minions:
r = gpg.encrypt(data, minion)
print r.ok
print r.stderr
r = client.cmd(minion, 'grains.setval', [grain_id, r.data], timeout=30)
print r
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment