def build_private_data_files(self, instance, **kwargs): ''' Creates temporary files containing the private data. Returns a dictionary i.e., { 'credentials': { <cyborgbackup.main.models.Credential>: '/path/to/decrypted/data', <cyborgbackup.main.models.Credential>: '/path/to/decrypted/data', <cyborgbackup.main.models.Credential>: '/path/to/decrypted/data', } } ''' private_data = self.build_private_data(instance, **kwargs) private_data_files = {'credentials': {}} if private_data is not None: ssh_ver = get_ssh_version() ssh_too_old = True if ssh_ver == "unknown" else Version( ssh_ver) < Version("6.0") openssh_keys_supported = ssh_ver != "unknown" and Version( ssh_ver) >= Version("6.5") for sets, data in private_data.get('credentials', {}).items(): # Bail out now if a private key was provided in OpenSSH format # and we're running an earlier version (<6.5). if 'OPENSSH PRIVATE KEY' in data and not openssh_keys_supported: raise RuntimeError(OPENSSH_KEY_ERROR) listpaths = [] for sets, data in private_data.get('credentials', {}).items(): # OpenSSH formatted keys must have a trailing newline to be # accepted by ssh-add. if 'OPENSSH PRIVATE KEY' in data and not data.endswith('\n'): data += '\n' # For credentials used with ssh-add, write to a named pipe which # will be read then closed, instead of leaving the SSH key on disk. if sets and not ssh_too_old: name = 'credential_{}'.format(sets.key) path = os.path.join(kwargs['private_data_dir'], name) run.open_fifo_write(path, data) listpaths.append(path) if len(listpaths) > 1: private_data_files['credentials']['ssh'] = listpaths elif len(listpaths) == 1: private_data_files['credentials']['ssh'] = listpaths[0] return private_data_files
def _run(self, cmd, sudo=False, queryargs=None): args = [] rc = -1 finalOutput = [] if self.client_user != 'root' and sudo: args = ['sudo', '-E'] + args args += cmd kwargs = {} try: env = {} for attr in dir(settings): if attr == attr.upper() and attr.startswith('CYBORGBACKUP_'): env[attr] = str(getattr(settings, attr)) path = tempfile.mkdtemp(prefix='cyborgbackup_module', dir='/tmp/') os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) kwargs['private_data_dir'] = path if 'private_data_dir' in kwargs.keys(): env['PRIVATE_DATA_DIR'] = kwargs['private_data_dir'] passwords = {} for setting in Setting.objects.filter(key__contains='ssh_key'): set = Setting.objects.get( key=setting.key.replace('ssh_key', 'ssh_password')) passwords['credential_{}'.format(setting.key)] = decrypt_field( set, 'value') kwargs['passwords'] = passwords private_data = {'credentials': {}} for sets in Setting.objects.filter(key__contains='ssh_key'): # If we were sent SSH credentials, decrypt them and send them # back (they will be written to a temporary file). private_data['credentials'][sets] = decrypt_field( sets, 'value') or '' private_data_files = {'credentials': {}} if private_data is not None: listpaths = [] for sets, data in private_data.get('credentials', {}).items(): # OpenSSH formatted keys must have a trailing newline to be # accepted by ssh-add. if 'OPENSSH PRIVATE KEY' in data and not data.endswith( '\n'): data += '\n' # For credentials used with ssh-add, write to a named pipe which # will be read then closed, instead of leaving the SSH key on disk. if sets: name = 'credential_{}'.format(sets.key) path = os.path.join(kwargs['private_data_dir'], name) run.open_fifo_write(path, data) listpaths.append(path) if len(listpaths) > 1: private_data_files['credentials']['ssh'] = listpaths elif len(listpaths) == 1: private_data_files['credentials']['ssh'] = listpaths[0] kwargs['private_data_files'] = private_data_files # May have to serialize the value kwargs['private_data_files'] = private_data_files cwd = '/tmp' new_args = [] new_args += [ 'ssh', '-Ao', 'StrictHostKeyChecking=no', '-o', 'UserKnownHostsFile=/dev/null' ] new_args += [ '{}@{}'.format(self.client_user, self.client.hostname) ] new_args += [ '\"echo \'####CYBMOD#####\';', ' '.join(args), '; exitcode=\$?; echo \'####CYBMOD#####\'; exit \$exitcode\"' ] args = new_args # If there is an SSH key path defined, wrap args with ssh-agent. private_data_files = kwargs.get('private_data_files', {}) if 'ssh' in private_data_files.get('credentials', {}): ssh_key_path = private_data_files['credentials']['ssh'] else: ssh_key_path = '' if ssh_key_path: ssh_auth_sock = os.path.join(kwargs['private_data_dir'], 'ssh_auth.sock') args = run.wrap_args_with_ssh_agent(args, ssh_key_path, ssh_auth_sock) # args = cmd expect_passwords = {} d = {} for k, v in kwargs['passwords'].items(): d[re.compile(r'Enter passphrase for .*' + k + r':\s*?$', re.M)] = k d[re.compile(r'Enter passphrase for .*' + k, re.M)] = k d[re.compile(r'Bad passphrase, try again for .*:\s*?$', re.M)] = '' for k, v in d.items(): expect_passwords[k] = kwargs['passwords'].get(v, '') or '' if queryargs and 'password' in queryargs.keys(): expect_passwords[re.compile(r'Enter password: \s*?$', re.M)] = queryargs['password'] stdout_handle = io.StringIO() _kw = dict( expect_passwords=expect_passwords, job_timeout=120, idle_timeout=None, pexpect_timeout=getattr(settings, 'PEXPECT_TIMEOUT', 5), ) status, rc = run.run_pexpect(args, cwd, env, stdout_handle, **_kw) stdout_handle.flush() output = stdout_handle.getvalue().split('\r\n') finalOutput = [] start = False for line in output: if 'Enter password: '******'Enter password: '******'') if line == '####CYBMOD#####' and not start: start = True if start and line != '####CYBMOD#####' and line != '': finalOutput += [line] shutil.rmtree(kwargs['private_data_dir']) except Exception: tb = traceback.format_exc() if settings.DEBUG: logger.exception('Exception occurred while running task') finally: try: logger.info('finished running, producing events.') except Exception: logger.exception( 'Error flushing stdout and saving event count.') print(rc) print(status) return finalOutput, rc