def pre_start(self, **kwargs): if not self.can_start: msg = u'%s is not in a startable state: %s, expecting one of %s' % ( self._meta.verbose_name, self.status, str(('new', 'waiting'))) self.job_explanation = msg self.save(update_fields=['job_explanation']) return (False, None) needed = self.get_passwords_needed_to_start() try: start_args = json.loads(decrypt_field(self, 'start_args')) except Exception: start_args = None if start_args in (None, ''): start_args = kwargs opts = dict([(field, start_args.get(field, '')) for field in needed]) if not all(opts.values()): missing_fields = ', '.join([k for k, v in opts.items() if not v]) self.job_explanation = u'Missing needed fields: %s.' % missing_fields self.save(update_fields=['job_explanation']) return (False, None) return (True, opts)
def get_value(self, name): objs = self.objects.filter(key=name) if len(objs) == 1: setting = objs[0] return decrypt_field(setting, 'value') else: return None
def build_passwords(self, job, **kwargs): ''' Build a dictionary of passwords for SSH private key, SSH user, sudo/su. ''' 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') return passwords
def build_private_data(self, instance, **kwargs): ''' Return SSH private key data (only if stored in DB as ssh_key_data). Return structure is a dict of the form: ''' 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 '' return private_data
def has_encrypted_ssh_key_data(self): if self.pk: ssh_key_data = decrypt_field(self, 'ssh_key_data') else: ssh_key_data = self.ssh_key_data try: pem_objects = validate_ssh_private_key(ssh_key_data) for pem_object in pem_objects: if pem_object.get('key_enc', False): return True except ValidationError: pass return False
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