Adam Tengler | b892d85 | 2017-08-24 15:07:08 +0000 | [diff] [blame] | 1 | from __future__ import absolute_import |
| 2 | |
| 3 | # Import python libs |
| 4 | import logging |
| 5 | import os |
| 6 | |
| 7 | try: |
| 8 | import paramiko |
| 9 | HAS_PARAMIKO = True |
| 10 | except: |
| 11 | HAS_PARAMIKO = False |
| 12 | |
| 13 | # Import Salt libs |
| 14 | import salt.config |
| 15 | import salt.wheel |
| 16 | |
| 17 | LOG = logging.getLogger(__name__) |
| 18 | |
| 19 | |
| 20 | def __virtual__(): |
| 21 | ''' |
| 22 | Only load if paramiko library exist. |
| 23 | ''' |
| 24 | if not HAS_PARAMIKO: |
| 25 | return ( |
| 26 | False, |
| 27 | 'Can not load module saltkey: paramiko library not found') |
| 28 | return True |
| 29 | |
| 30 | |
| 31 | def key_create(id_, host, force=False): |
| 32 | ''' |
| 33 | Generates minion keypair, accepts it on master and injects it to minion via SSH. |
| 34 | |
Adam Tengler | 6218896 | 2017-09-04 13:34:44 +0000 | [diff] [blame] | 35 | :param id_: expected minion ID of target node |
| 36 | :param host: IP address or resolvable hostname/FQDN of target node |
| 37 | |
Adam Tengler | b892d85 | 2017-08-24 15:07:08 +0000 | [diff] [blame] | 38 | CLI Examples: |
| 39 | |
| 40 | .. code-block:: bash |
| 41 | |
| 42 | salt-call saltkey.key_create <MINION_ID> <MINION_IP_ADDRESS> force=False |
| 43 | ''' |
| 44 | ret = { |
| 45 | 'retcode': 0, |
| 46 | 'msg': 'Salt Key for minion %s is already accepted' % id_, |
| 47 | } |
| 48 | |
| 49 | opts = salt.config.master_config('/etc/salt/master') |
| 50 | wheel = salt.wheel.WheelClient(opts) |
| 51 | keys = wheel.cmd('key.gen_accept', arg=[id_], kwarg={'force': force}) |
| 52 | pub_key = keys.get('pub', None) |
| 53 | priv_key = keys.get('priv', None) |
| 54 | |
| 55 | if pub_key and priv_key: |
| 56 | ssh = paramiko.SSHClient() |
| 57 | ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
| 58 | # Establish SSH connection to minion |
| 59 | try: |
| 60 | ssh.connect(host) |
| 61 | except paramiko.ssh_exception.AuthenticationException: |
| 62 | msg = ('Could not establish SSH connection to minion "%s" on address %s, please ensure ' |
| 63 | 'that current user\'s SSH key is present in minions authorized_keys.') % (id_, host) |
| 64 | LOG.error(msg) |
| 65 | ret['retcode'] = 1 |
| 66 | ret['msg'] = msg |
| 67 | wheel.cmd_async({'fun': 'key.delete', 'match': id_}) |
| 68 | return ret |
| 69 | except Exception as e: |
| 70 | msg = ('Unknown error occured while establishing SSH connection ' |
| 71 | 'to minion "%s" on address %s: %s') % (id_, host, repr(e)) |
| 72 | LOG.error(msg) |
| 73 | ret['retcode'] = 1 |
| 74 | ret['msg'] = msg |
| 75 | wheel.cmd_async({'fun': 'key.delete', 'match': id_}) |
| 76 | return ret |
| 77 | # Setup the keys on minion side the ugly way, nice one didn't work |
| 78 | key_path = '/etc/salt/pki/minion' |
| 79 | command = ('echo "%(pub_key)s" > %(pub_path)s && chmod 644 %(pub_path)s && ' |
| 80 | 'echo "%(priv_key)s" > %(priv_path)s && chmod 400 %(priv_path)s && ' |
| 81 | 'salt-call --local service.restart salt-minion') % { |
| 82 | 'pub_path': os.path.join(key_path, 'minion.pub'), |
| 83 | 'pub_key': pub_key, |
| 84 | 'priv_path': os.path.join(key_path, 'minion.pem'), |
| 85 | 'priv_key': priv_key |
| 86 | } |
| 87 | |
| 88 | ssh_chan = ssh.get_transport().open_session() |
| 89 | ssh_chan.exec_command(command) |
| 90 | # Wait for command return |
| 91 | while True: |
| 92 | if ssh_chan.exit_status_ready(): |
| 93 | exit_status = ssh_chan.recv_exit_status() |
| 94 | stderr = ssh_chan.recv_stderr(1000) |
| 95 | stdout = ssh_chan.recv(1000) |
| 96 | break |
| 97 | ssh.close() |
| 98 | # Evaluate SSH command exit status |
| 99 | if exit_status != 0: |
| 100 | msg = 'Keypair injection to Salt minion failed on target with following error: %s' % stderr |
| 101 | LOG.error(msg) |
| 102 | ret['retcode'] = exit_status |
| 103 | ret['msg'] = msg |
| 104 | return ret |
| 105 | |
| 106 | ret['msg'] = 'Salt Key successfully created' |
| 107 | |
| 108 | return ret |
| 109 | |