|
|
|
|
|
|
|
|
|
|
|
from __future__ import absolute_import |
|
|
|
|
|
|
|
|
|
|
|
# Import python libs |
|
|
|
|
|
import logging |
|
|
|
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
import paramiko |
|
|
|
|
|
HAS_PARAMIKO = True |
|
|
|
|
|
except: |
|
|
|
|
|
HAS_PARAMIKO = False |
|
|
|
|
|
|
|
|
|
|
|
# Import Salt libs |
|
|
|
|
|
import salt.config |
|
|
|
|
|
import salt.wheel |
|
|
|
|
|
|
|
|
|
|
|
LOG = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __virtual__(): |
|
|
|
|
|
''' |
|
|
|
|
|
Only load if paramiko library exist. |
|
|
|
|
|
''' |
|
|
|
|
|
if not HAS_PARAMIKO: |
|
|
|
|
|
return ( |
|
|
|
|
|
False, |
|
|
|
|
|
'Can not load module saltkey: paramiko library not found') |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def key_create(id_, host, force=False): |
|
|
|
|
|
''' |
|
|
|
|
|
Generates minion keypair, accepts it on master and injects it to minion via SSH. |
|
|
|
|
|
|
|
|
|
|
|
CLI Examples: |
|
|
|
|
|
|
|
|
|
|
|
.. code-block:: bash |
|
|
|
|
|
|
|
|
|
|
|
salt-call saltkey.key_create <MINION_ID> <MINION_IP_ADDRESS> force=False |
|
|
|
|
|
''' |
|
|
|
|
|
ret = { |
|
|
|
|
|
'retcode': 0, |
|
|
|
|
|
'msg': 'Salt Key for minion %s is already accepted' % id_, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
opts = salt.config.master_config('/etc/salt/master') |
|
|
|
|
|
wheel = salt.wheel.WheelClient(opts) |
|
|
|
|
|
keys = wheel.cmd('key.gen_accept', arg=[id_], kwarg={'force': force}) |
|
|
|
|
|
pub_key = keys.get('pub', None) |
|
|
|
|
|
priv_key = keys.get('priv', None) |
|
|
|
|
|
|
|
|
|
|
|
if pub_key and priv_key: |
|
|
|
|
|
ssh = paramiko.SSHClient() |
|
|
|
|
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
|
|
|
|
|
# Establish SSH connection to minion |
|
|
|
|
|
try: |
|
|
|
|
|
ssh.connect(host) |
|
|
|
|
|
except paramiko.ssh_exception.AuthenticationException: |
|
|
|
|
|
msg = ('Could not establish SSH connection to minion "%s" on address %s, please ensure ' |
|
|
|
|
|
'that current user\'s SSH key is present in minions authorized_keys.') % (id_, host) |
|
|
|
|
|
LOG.error(msg) |
|
|
|
|
|
ret['retcode'] = 1 |
|
|
|
|
|
ret['msg'] = msg |
|
|
|
|
|
wheel.cmd_async({'fun': 'key.delete', 'match': id_}) |
|
|
|
|
|
return ret |
|
|
|
|
|
except Exception as e: |
|
|
|
|
|
msg = ('Unknown error occured while establishing SSH connection ' |
|
|
|
|
|
'to minion "%s" on address %s: %s') % (id_, host, repr(e)) |
|
|
|
|
|
LOG.error(msg) |
|
|
|
|
|
ret['retcode'] = 1 |
|
|
|
|
|
ret['msg'] = msg |
|
|
|
|
|
wheel.cmd_async({'fun': 'key.delete', 'match': id_}) |
|
|
|
|
|
return ret |
|
|
|
|
|
# Setup the keys on minion side the ugly way, nice one didn't work |
|
|
|
|
|
key_path = '/etc/salt/pki/minion' |
|
|
|
|
|
command = ('echo "%(pub_key)s" > %(pub_path)s && chmod 644 %(pub_path)s && ' |
|
|
|
|
|
'echo "%(priv_key)s" > %(priv_path)s && chmod 400 %(priv_path)s && ' |
|
|
|
|
|
'salt-call --local service.restart salt-minion') % { |
|
|
|
|
|
'pub_path': os.path.join(key_path, 'minion.pub'), |
|
|
|
|
|
'pub_key': pub_key, |
|
|
|
|
|
'priv_path': os.path.join(key_path, 'minion.pem'), |
|
|
|
|
|
'priv_key': priv_key |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
ssh_chan = ssh.get_transport().open_session() |
|
|
|
|
|
ssh_chan.exec_command(command) |
|
|
|
|
|
# Wait for command return |
|
|
|
|
|
while True: |
|
|
|
|
|
if ssh_chan.exit_status_ready(): |
|
|
|
|
|
exit_status = ssh_chan.recv_exit_status() |
|
|
|
|
|
stderr = ssh_chan.recv_stderr(1000) |
|
|
|
|
|
stdout = ssh_chan.recv(1000) |
|
|
|
|
|
break |
|
|
|
|
|
ssh.close() |
|
|
|
|
|
# Evaluate SSH command exit status |
|
|
|
|
|
if exit_status != 0: |
|
|
|
|
|
msg = 'Keypair injection to Salt minion failed on target with following error: %s' % stderr |
|
|
|
|
|
LOG.error(msg) |
|
|
|
|
|
ret['retcode'] = exit_status |
|
|
|
|
|
ret['msg'] = msg |
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
|
|
ret['msg'] = 'Salt Key successfully created' |
|
|
|
|
|
|
|
|
|
|
|
return ret |
|
|
|
|
|
|