def generate_vm_conf(install_info: InstallInfo, vm_id: int): with open(os.path.join(ETC_DIR, 'scripts/cfg.template'), 'r') as f: template = f.read() storage_backend = get_storage_backend(install_info) disks = [] disks.append(storage_backend.get_vm_disk_path(vm_id)) disks.append('file:{iso},hdc:cdrom,r'.format( iso=os.path.abspath(install_info.iso_path))) if install_info.enable_unattended: disks.append( 'file:{main_dir}/volumes/unattended.iso,hdd:cdrom,r'.format( main_dir=LIB_DIR)) disks = ', '.join(['"{}"'.format(disk) for disk in disks]) template = template.replace('{{ VM_ID }}', str(vm_id)) template = template.replace('{{ DISKS }}', disks) template = template.replace('{{ VNC_PORT }}', str(6400 + vm_id)) if vm_id == 0: template = re.sub('on_reboot[ ]*=(.*)', 'on_reboot = "restart"', template) with open(os.path.join(ETC_DIR, 'configs/vm-{}.cfg'.format(vm_id)), 'w') as f: f.write(template) logging.info( "Generated VM configuration for vm-{vm_id}".format(vm_id=vm_id))
def snapshot_import(name, bucket, full, zpool): local_install = InstallInfo.try_load() if local_install is not None: click.confirm( "Detected local snapshot. It will be REMOVED. Continue?", abort=True ) mc = get_minio_client(conf) if not mc.bucket_exists(bucket): logging.error("Bucket %s doesn't exist", bucket) return ensure_dirs() try: if full: logging.warning( "Importing full snapshot. This may not work if hardware is different" ) do_import_full(mc, name, bucket, zpool) else: do_import_minimal(mc, name, bucket, zpool) # This could probably use some refactoring # We're duplicating quite a lot of code from install function install_info = InstallInfo.load() generate_vm_conf(install_info, 0) backend = get_storage_backend(install_info) backend.rollback_vm_storage(0) net_enable = int(conf["drakrun"].get("net_enable", "0")) out_interface = conf["drakrun"].get("out_interface", "") dns_server = conf["drakrun"].get("dns_server", "") setup_vm_network( vm_id=0, net_enable=net_enable, out_interface=out_interface, dns_server=dns_server, ) if net_enable: start_dnsmasq(vm_id=0, dns_server=dns_server, background=True) cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg") try: subprocess.run(["xl" "create", cfg_path], check=True) except subprocess.CalledProcessError: logging.exception("Failed to launch VM vm-0") return logging.info("Minimal snapshots require postinstall to work correctly") logging.info( "Please VNC to the port 5900 to ensure the OS booted correctly" ) logging.info("After that, execute this command to finish the setup") logging.info("# draksetup postinstall") except NoSuchKey: logging.error("Import failed. Missing files in bucket.")
def run_vm(self): try: subprocess.check_output(["xl", "destroy", self.vm_name], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: pass storage_backend = get_storage_backend(self.install_info) snapshot_version = storage_backend.get_vm0_snapshot_time() storage_backend.rollback_vm_storage(self.instance_id) try: subprocess.run([ "xl", "-vvv", "restore", os.path.join(VM_CONFIG_DIR, f"{self.vm_name}.cfg"), os.path.join(VOLUME_DIR, "snapshot.sav") ], check=True) except subprocess.CalledProcessError: logging.exception(f"Failed to restore VM {self.vm_name}") with open(f"/var/log/xen/qemu-dm-{self.vm_name}.log", "rb") as f: logging.error(f.read()) return snapshot_version
def generate_vm_conf(install_info: InstallInfo, vm_id: int): with open(os.path.join(ETC_DIR, 'scripts', 'cfg.template'), 'r') as f: template = f.read() storage_backend = get_storage_backend(install_info) disks = [] disks.append(storage_backend.get_vm_disk_path(vm_id)) install_iso_path = os.path.abspath(install_info.iso_path) disks.append(f'file:{install_iso_path},{FIRST_CDROM_DRIVE}:cdrom,r') if install_info.enable_unattended: unattended_iso_path = os.path.join(LIB_DIR, 'volumes', 'unattended.iso') disks.append( f'file:{unattended_iso_path},{SECOND_CDROM_DRIVE}:cdrom,r') disks = ', '.join(['"{}"'.format(disk) for disk in disks]) template = template.replace('{{ VM_ID }}', str(vm_id)) template = template.replace('{{ DISKS }}', disks) template = template.replace('{{ VNC_PORT }}', str(6400 + vm_id)) if vm_id == 0: template = re.sub('on_reboot[ ]*=(.*)', 'on_reboot = "restart"', template) with open(os.path.join(ETC_DIR, 'configs', f'vm-{vm_id}.cfg'), 'w') as f: f.write("# Autogenerated, don't edit this file directly!\n") f.write("# Instead please edit /etc/drakrun/scripts/cfg.template\n") f.write("# and restart drakrun@<vm_id> service.\n\n") f.write(template) log.info("Generated VM configuration for vm-{vm_id}".format(vm_id=vm_id))
def run_vm(vm_id): install_info = InstallInfo.load() try: subprocess.check_output( ["xl", "destroy", "vm-{vm_id}".format(vm_id=vm_id)], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: pass storage_backend = get_storage_backend(install_info) snapshot_version = storage_backend.get_vm0_snapshot_time() storage_backend.rollback_vm_storage(vm_id) try: subprocess.run([ "xl", "-vvv", "restore", os.path.join(ETC_DIR, "configs/vm-{vm_id}.cfg".format(vm_id=vm_id)), os.path.join(LIB_DIR, "volumes/snapshot.sav") ], check=True) except subprocess.CalledProcessError: logging.exception( "Failed to restore VM {vm_id}".format(vm_id=vm_id)) with open( "/var/log/xen/qemu-dm-vm-{vm_id}.log".format(vm_id=vm_id), "rb") as f: logging.error(f.read()) return snapshot_version
def do_import_minimal(mc, name, bucket, zpool): """ Perform minimal snapshot import, symmetric to do_export_minimal """ logging.info("Downloading installation info") mc.fget_object(bucket, f"{name}/install.json", os.path.join(ETC_DIR, InstallInfo._INSTALL_FILENAME)) logging.info("Downloading VM config") mc.fget_object(bucket, f"{name}/cfg.template", os.path.join(ETC_DIR, "scripts", "cfg.template")) # Now we have imported InstallInfo object install_info = InstallInfo.load() # Patch ZFS pool name if zpool is not None: install_info.zfs_tank_name = zpool # Save patched ZFS dataset name (storage backend has to know it) install_info.save() storage = get_storage_backend(install_info) with tempfile.NamedTemporaryFile() as disk_image: logging.info("Downloading VM disk image") mc.fget_object(bucket, f"{name}/disk.img", disk_image.name) logging.info("Importing VM disk") storage.import_vm0(disk_image.name)
def create_missing_profiles(): """ Creates usermode profiles by restoring vm-1 and extracting the DLLs. Assumes that injector is configured properly, i.e. kernel and runtime profiles exist and that vm-1 is free to use. """ # Prepare injector with open(os.path.join(PROFILE_DIR, "runtime.json"), "r") as runtime_f: runtime_info = RuntimeInfo.load(runtime_f) kernel_profile = os.path.join(PROFILE_DIR, "kernel.json") injector = Injector("vm-1", runtime_info, kernel_profile) # restore vm-1 out_interface = conf["drakrun"].get("out_interface", "") dns_server = conf["drakrun"].get("dns_server", "") install_info = InstallInfo.load() backend = get_storage_backend(install_info) generate_vm_conf(install_info, 1) setup_vm_network(vm_id=1, net_enable=False, out_interface=out_interface, dns_server=dns_server) vm = VirtualMachine(backend, 1) vm.restore() # Ensure that all declared usermode profiles exist # This is important when upgrade defines new entries in dll_file_list and compulsory_dll_file_list for profile in compulsory_dll_file_list: if not profiles_exist(profile.dest): create_rekall_profile(injector, profile, True) for profile in dll_file_list: if not profiles_exist(profile.dest): try: create_rekall_profile(injector, profile) except Exception: log.exception( "Unexpected exception from create_rekall_profile!") build_os_info(APISCOUT_PROFILE_DIR, vmi_win_guid(vm.vm_name), backend) dll_basename_list = [dll.dest for dll in dll_file_list] static_apiscout_profile = build_static_apiscout_profile( APISCOUT_PROFILE_DIR, dll_basename_list) with open( Path(APISCOUT_PROFILE_DIR) / "static_apiscout_profile.json", "w") as f: json.dump(static_apiscout_profile, f) vm.destroy() delete_vm_network(vm_id=1, net_enable=False, out_interface=out_interface, dns_server=dns_server)
def memdump_export(bucket, instance): install_info = InstallInfo.try_load() if install_info is None: logging.error( "Missing installation info. Did you forget to set up the sandbox?") return backend = get_storage_backend(install_info) vm = VirtualMachine(backend, instance) if vm.is_running: logging.exception(f"vm-{instance} is running") return logging.info("Calculating snapshot hash...") snapshot_sha256 = file_sha256(os.path.join(VOLUME_DIR, "snapshot.sav")) name = f"{snapshot_sha256}_pre_sample.raw_memdump.gz" mc = get_minio_client(conf) if not mc.bucket_exists(bucket): logging.error("Bucket %s doesn't exist", bucket) return try: mc.stat_object(bucket, name) logging.info("This file already exists in specified bucket") return except NoSuchKey: pass except Exception: logging.exception("Failed to check if object exists on minio") logging.info("Restoring VM and performing memory dump") try: vm.restore(pause=True) except subprocess.CalledProcessError: logging.exception(f"Failed to restore VM {vm.vm_name}") with open(f"/var/log/xen/qemu-dm-{vm.vm_name}.log", "rb") as f: logging.error(f.read()) logging.info("VM restored") with tempfile.NamedTemporaryFile() as compressed_memdump: vm.memory_dump(compressed_memdump.name) logging.info(f"Uploading {name} to {bucket}") mc.fput_object(bucket, name, compressed_memdump.name) try: vm.destroy() except Exception: logging.exception("Failed to destroy VM") logging.info("Done")
def postupgrade(): if not check_root(): return with open(os.path.join(ETC_DIR, 'scripts/cfg.template'), 'r') as f: template = f.read() passwd_characters = string.ascii_letters + string.digits passwd = ''.join(secrets.choice(passwd_characters) for _ in range(20)) template = template.replace('{{ VNC_PASS }}', passwd) with open(os.path.join(ETC_DIR, 'scripts', 'cfg.template'), 'w') as f: f.write(template) detect_defaults() install_info = InstallInfo.try_load() if not install_info: logging.info("Postupgrade done. DRAKVUF Sandbox not installed.") return # Prepare injector with open(os.path.join(PROFILE_DIR, "runtime.json"), 'r') as runtime_f: runtime_info = RuntimeInfo.load(runtime_f) kernel_profile = os.path.join(PROFILE_DIR, "kernel.json") injector = Injector('vm-1', runtime_info, kernel_profile) stop_all_drakruns() # Use vm-1 for generating profiles out_interface = conf['drakrun'].get('out_interface', '') dns_server = conf['drakrun'].get('dns_server', '') setup_vm_network(vm_id=1, net_enable=False, out_interface=out_interface, dns_server=dns_server) backend = get_storage_backend(install_info) vm = VirtualMachine(backend, 1) vm.restore() create_missing_profiles(injector) vm.destroy() delete_vm_network(vm_id=1, net_enable=False, out_interface=out_interface, dns_server=dns_server) start_enabled_drakruns()
def create_rekall_profiles(install_info: InstallInfo): storage_backend = get_storage_backend(install_info) with storage_backend.vm0_root_as_block() as block_device, \ tempfile.TemporaryDirectory() as mount_path: mnt_path_quoted = shlex.quote(mount_path) blk_quoted = shlex.quote(block_device) try: subprocess.check_output( f"mount -t ntfs -o ro {blk_quoted} {mnt_path_quoted}", shell=True) except subprocess.CalledProcessError: raise RuntimeError(f"Failed to mount {block_device} as NTFS.") profiles_path = os.path.join(LIB_DIR, "profiles") for file in dll_file_list: try: logging.info(f"Fetching rekall profile for {file.path}") local_dll_path = os.path.join(profiles_path, file.dest) copyfile(os.path.join(mount_path, file.path), local_dll_path) guid = pdb_guid(local_dll_path) tmp = fetch_pdb(guid["filename"], guid["GUID"], profiles_path) logging.debug("Parsing PDB into JSON profile...") profile = make_pdb_profile(tmp) with open(os.path.join(profiles_path, f"{file.dest}.json"), 'w') as f: f.write(profile) except FileNotFoundError: logging.warning( f"Failed to copy file {file.path}, skipping...") except RuntimeError: logging.warning( f"Failed to fetch profile for {file.path}, skipping...") except Exception: logging.warning( f"Unexpected exception while creating rekall profile for {file.path}, skipping..." ) finally: if os.path.exists(local_dll_path): os.remove(local_dll_path) if os.path.exists(os.path.join(profiles_path, tmp)): os.remove(os.path.join(profiles_path, tmp)) # cleanup subprocess.check_output(f'umount {mnt_path_quoted}', shell=True)
def create_missing_profiles(): """ Creates usermode profiles by restoring vm-1 and extracting the DLLs. Assumes that injector is configured properly, i.e. kernel and runtime profiles exist and that vm-1 is free to use. """ # Prepare injector with open(os.path.join(PROFILE_DIR, "runtime.json"), "r") as runtime_f: runtime_info = RuntimeInfo.load(runtime_f) kernel_profile = os.path.join(PROFILE_DIR, "kernel.json") injector = Injector("vm-1", runtime_info, kernel_profile) # restore vm-1 out_interface = conf["drakrun"].get("out_interface", "") dns_server = conf["drakrun"].get("dns_server", "") install_info = InstallInfo.load() backend = get_storage_backend(install_info) generate_vm_conf(install_info, 1) setup_vm_network(vm_id=1, net_enable=False, out_interface=out_interface, dns_server=dns_server) vm = VirtualMachine(backend, 1) vm.restore() # Ensure that all declared usermode profiles exist # This is important when upgrade defines new entries in dll_file_list and compulsory_dll_file_list for profile in compulsory_dll_file_list: if not profile_exists(profile): create_rekall_profile(injector, profile, True) for profile in dll_file_list: if not profile_exists(profile): try: create_rekall_profile(injector, profile) except Exception: # silence per-dll errors pass vm.destroy() delete_vm_network(vm_id=1, net_enable=False, out_interface=out_interface, dns_server=dns_server)
def __init__(self, vm_id: int, dns: str): install_info = InstallInfo.load() backend = get_storage_backend(install_info) generate_vm_conf(install_info, vm_id) self.vm = VirtualMachine(backend, vm_id) with open(Path(PROFILE_DIR) / "runtime.json", 'r') as f: self.runtime_info = RuntimeInfo.load(f) self.desktop = WinPath(r"%USERPROFILE%") / "Desktop" self.kernel_profile = Path(PROFILE_DIR) / "kernel.json" self.injector = Injector( self.vm.vm_name, self.runtime_info, self.kernel_profile, ) setup_vm_network(vm_id, True, find_default_interface(), dns)
def do_export_minimal(mc, bucket, name): """ Perform minimal snapshot export, symmetric to do_import_minimal """ logging.info("Uploading installation info") install_info = InstallInfo.load() install_data = json.dumps(install_info.to_dict()).encode() mc.put_object(bucket, f"{name}/install.json", io.BytesIO(install_data), len(install_data)) logging.info("Uploading VM template") mc.fput_object(bucket, f"{name}/cfg.template", os.path.join(ETC_DIR, "scripts", "cfg.template")) with tempfile.NamedTemporaryFile() as disk_image: logging.info("Exporting VM hard drive") storage = get_storage_backend(install_info) storage.export_vm0(disk_image.name) logging.info("Uploading disk.img") mc.fput_object(bucket, f"{name}/disk.img", disk_image.name)
def run_vm(self): backend = get_storage_backend(self.install_info) vm = VirtualMachine(backend, self.instance_id) try: vm.restore() except subprocess.CalledProcessError: self.log.exception(f"Failed to restore VM {self.vm_name}") with open(f"/var/log/xen/qemu-dm-{self.vm_name}.log", "rb") as f: logging.error(f.read()) self.log.info("VM restored") try: yield vm finally: try: vm.destroy() except Exception: self.log.exception("Failed to destroy VM")
def cleanup(): if not check_root(): return install_info = InstallInfo.try_load() if install_info is None: logging.error("The cleanup has been performed") return stop_all_drakruns() backend = get_storage_backend(install_info) vm_ids = get_all_vm_conf() net_enable = int(conf["drakrun"].get("net_enable", "0")) out_interface = conf["drakrun"].get("out_interface", "") dns_server = conf["drakrun"].get("dns_server", "") for vm_id in vm_ids: vm = VirtualMachine(backend, vm_id) vm.destroy() delete_vm_network( vm_id=vm_id, net_enable=net_enable, out_interface=out_interface, dns_server=dns_server, ) if net_enable: stop_dnsmasq(vm_id=vm_id) backend.delete_vm_volume(vm_id) delete_vm_conf(vm_id) safe_delete(os.path.join(VOLUME_DIR, "snapshot.sav")) cleanup_postinstall_files() InstallInfo.delete()
def generate_vm_conf(install_info: InstallInfo, vm_id: int): with open(os.path.join(ETC_DIR, "scripts", "cfg.template"), "r") as f: template = f.read() storage_backend = get_storage_backend(install_info) disks = [] disks.append(storage_backend.get_vm_disk_path(vm_id)) install_iso_path = os.path.abspath(install_info.iso_path) disks.append(f"file:{install_iso_path},{FIRST_CDROM_DRIVE}:cdrom,r") if install_info.enable_unattended: unattended_iso_path = os.path.join(LIB_DIR, "volumes", "unattended.iso") disks.append( f"file:{unattended_iso_path},{SECOND_CDROM_DRIVE}:cdrom,r") disks = ", ".join(['"{}"'.format(disk) for disk in disks]) template = template.replace("{{ VM_ID }}", str(vm_id)) template = template.replace("{{ DISKS }}", disks) template = template.replace("{{ VNC_PORT }}", str(6400 + vm_id)) template = template.replace("{{ VCPUS }}", str(install_info.vcpus)) template = template.replace("{{ MEMORY }}", str(install_info.memory)) if vm_id == 0: template = re.sub("on_reboot[ ]*=(.*)", 'on_reboot = "restart"', template) with open(os.path.join(ETC_DIR, "configs", f"vm-{vm_id}.cfg"), "w") as f: f.write("# Autogenerated, don't edit this file directly!\n") f.write("# Instead please edit /etc/drakrun/scripts/cfg.template\n") f.write("# and restart drakrun@<vm_id> service.\n\n") f.write(template) log.info("Generated VM configuration for vm-{vm_id}".format(vm_id=vm_id))
def postinstall(report, generate_usermode): if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")): report = False install_info = InstallInfo.load() max_vms = install_info.max_vms output = subprocess.check_output(['vmi-win-guid', 'name', 'vm-0'], timeout=30).decode('utf-8') try: version = re.search(r'Version: (.*)', output).group(1) pdb = re.search(r'PDB GUID: ([0-9a-f]+)', output).group(1) fn = re.search(r'Kernel filename: ([a-z]+\.[a-z]+)', output).group(1) except AttributeError: logging.error("Failed to obtain kernel PDB GUID/Kernel filename.") return logging.info("Determined PDB GUID: {}".format(pdb)) logging.info("Determined kernel filename: {}".format(fn)) logging.info("Fetching PDB file...") dest = fetch_pdb(fn, pdb, destdir=os.path.join(LIB_DIR, 'profiles/')) logging.info("Generating profile out of PDB file...") profile = make_pdb_profile(dest) logging.info("Saving profile...") kernel_profile = os.path.join(LIB_DIR, 'profiles', 'kernel.json') with open(kernel_profile, 'w') as f: f.write(profile) output = subprocess.check_output( ['vmi-win-offsets', '--name', 'vm-0', '--json-kernel', kernel_profile], timeout=30).decode('utf-8') offsets = re.findall(r'^([a-z_]+):(0x[0-9a-f]+)$', output, re.MULTILINE) if not offsets: logging.error("Failed to parse output of vmi-win-offsets.") return offsets_dict = {k: v for k, v in offsets} if 'kpgd' not in offsets_dict: logging.error("Failed to obtain KPGD value.") return module_dir = os.path.dirname(os.path.realpath(__file__)) pid_tool = os.path.join(module_dir, "tools", "get-explorer-pid") explorer_pid_s = subprocess.check_output( [pid_tool, "vm-0", kernel_profile, offsets_dict['kpgd']], timeout=30).decode('ascii', 'ignore') m = re.search(r'explorer\.exe:([0-9]+)', explorer_pid_s) explorer_pid = m.group(1) runtime_profile = {"vmi_offsets": offsets_dict, "inject_pid": explorer_pid} logging.info("Saving runtime profile...") with open(os.path.join(LIB_DIR, 'profiles', 'runtime.json'), 'w') as f: f.write(json.dumps(runtime_profile, indent=4)) logging.info("Saving VM snapshot...") subprocess.check_output('xl save vm-0 ' + os.path.join(LIB_DIR, "volumes", "snapshot.sav"), shell=True) storage_backend = get_storage_backend(install_info) storage_backend.snapshot_vm0_volume() logging.info("Snapshot was saved succesfully.") if generate_usermode: try: create_rekall_profiles(install_info) except RuntimeError as e: logging.warning("Generating usermode profiles failed") logging.exception(e) for vm_id in range(max_vms + 1): # we treat vm_id=0 as special internal one generate_vm_conf(install_info, vm_id) if report: send_usage_report({ "kernel": { "guid": pdb, "filename": fn, "version": version }, "install_iso": { "sha256": install_info.iso_sha256 } }) reenable_services() logging.info("All right, drakrun setup is done.")
def install(storage_backend, disk_size, iso_path, zfs_tank_name, max_vms, unattended_xml): logging.info("Ensuring that drakrun@* services are stopped...") subprocess.check_output('systemctl stop \'drakrun@*\'', shell=True, stderr=subprocess.STDOUT) logging.info("Performing installation...") if unattended_xml: logging.info("Baking unattended.iso for automated installation") with tempfile.TemporaryDirectory() as tmpdir: tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml') with open(tmp_xml_path, 'wb') as fw: with open(unattended_xml, 'rb') as fr: fw.write(fr.read()) try: subprocess.check_output([ 'genisoimage', '-o', os.path.join(LIB_DIR, "volumes/unattended.iso"), '-J', '-r', tmp_xml_path ], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: logging.exception("Failed to generate unattended.iso.") sha256_hash = hashlib.sha256() with open(iso_path, "rb") as f: for byte_block in iter(lambda: f.read(4096), b""): sha256_hash.update(byte_block) iso_sha256 = sha256_hash.hexdigest() install_info = InstallInfo(storage_backend=storage_backend, disk_size=disk_size, iso_path=os.path.abspath(iso_path), zfs_tank_name=zfs_tank_name, max_vms=max_vms, enable_unattended=unattended_xml is not None, iso_sha256=iso_sha256) install_info.save() logging.info("Checking xen-detect...") proc = subprocess.run('xen-detect -N', shell=True) if proc.returncode != 1: logging.error( 'It looks like the system is not running on Xen. Please reboot your machine into Xen hypervisor.' ) return logging.info("Testing if xl tool is sane...") try: subprocess.check_output('xl info', shell=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError: logging.exception("Failed to test xl command.") return try: subprocess.check_output('xl uptime vm-0', shell=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError: pass else: logging.info('Detected that vm-0 is already running, stopping it.') subprocess.run('xl destroy vm-0', shell=True, check=True) generate_vm_conf(install_info, 0) backend = get_storage_backend(install_info) backend.initialize_vm0_volume(disk_size) try: subprocess.check_output("brctl show", shell=True) except subprocess.CalledProcessError: logging.exception( "Failed to execute brctl show. Make sure you have bridge-utils installed." ) return try: subprocess.check_output('brctl addbr drak0', stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError as e: if b'already exists' in e.output: logging.info("Bridge drak0 already exists.") else: logging.exception("Failed to create bridge drak0.") cfg_path = os.path.join(ETC_DIR, "configs/vm-0.cfg") try: subprocess.run('xl create {}'.format(shlex.quote(cfg_path)), shell=True, check=True) except subprocess.CalledProcessError: logging.exception("Failed to launch VM vm-0") return logging.info("-" * 80) logging.info("Initial VM setup is complete and the vm-0 was launched.") logging.info( "Please now VNC to the port 5900 on this machine to perform Windows installation." ) logging.info( "After you have installed Windows and booted it to the desktop, please execute:" ) logging.info("# draksetup postinstall") with open(cfg_path, "r") as f: data = f.read() m = re.search(r'vncpasswd[ ]*=(.*)', data) if m: passwd = m.group(1).strip() if passwd[0] == '"' and passwd[-1] == '"': passwd = passwd[1:-1] logging.info("Your configured VNC password is:") logging.info(passwd) logging.info( "Please note that on some machines, system installer may boot for up to 10 minutes" ) logging.info( "and may look unresponsive during the process. Please be patient.") logging.info("-" * 80)
def postinstall(report, generate_usermode): if not check_root(): return if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")): report = False install_info = InstallInfo.load() storage_backend = get_storage_backend(install_info) vm0 = VirtualMachine(storage_backend, 0) if vm0.is_running is False: logging.exception("vm-0 is not running") return logging.info("Cleaning up leftovers(if any)") cleanup_postinstall_files() logging.info("Ejecting installation CDs") eject_cd("vm-0", FIRST_CDROM_DRIVE) if install_info.enable_unattended: # If unattended install is enabled, we have an additional CD-ROM drive eject_cd("vm-0", SECOND_CDROM_DRIVE) kernel_info = vmi_win_guid("vm-0") logging.info(f"Determined PDB GUID: {kernel_info.guid}") logging.info(f"Determined kernel filename: {kernel_info.filename}") logging.info("Fetching PDB file...") dest = fetch_pdb(kernel_info.filename, kernel_info.guid, destdir=PROFILE_DIR) logging.info("Generating profile out of PDB file...") profile = make_pdb_profile(dest) logging.info("Saving profile...") kernel_profile = os.path.join(PROFILE_DIR, "kernel.json") with open(kernel_profile, "w") as f: f.write(profile) safe_delete(dest) vmi_offsets = extract_vmi_offsets("vm-0", kernel_profile) explorer_pid = extract_explorer_pid("vm-0", kernel_profile, vmi_offsets) runtime_info = RuntimeInfo(vmi_offsets=vmi_offsets, inject_pid=explorer_pid) logging.info("Saving runtime profile...") with open(os.path.join(PROFILE_DIR, "runtime.json"), "w") as f: f.write(runtime_info.to_json(indent=4)) logging.info("Saving VM snapshot...") # Create vm-0 snapshot, and destroy it # WARNING: qcow2 snapshot method is a noop. fresh images are created on the fly # so we can't keep the vm-0 running vm0.save(os.path.join(VOLUME_DIR, "snapshot.sav")) logging.info("Snapshot was saved succesfully.") # Memory state is frozen, we can't do any writes to persistent storage logging.info("Snapshotting persistent memory...") storage_backend.snapshot_vm0_volume() if report: send_usage_report({ "kernel": { "guid": kernel_info.guid, "filename": kernel_info.filename, "version": kernel_info.version, }, "install_iso": { "sha256": install_info.iso_sha256 }, }) if generate_usermode: # Restore a VM and create usermode profiles create_missing_profiles() logging.info("All right, drakrun setup is done.") logging.info("First instance of drakrun will be enabled automatically...") subprocess.check_output("systemctl enable drakrun@1", shell=True) subprocess.check_output("systemctl start drakrun@1", shell=True) logging.info("If you want to have more parallel instances, execute:") logging.info(" # draksetup scale <number of instances>")
def postinstall(report, generate_usermode): if not check_root(): return if os.path.exists(os.path.join(ETC_DIR, "no_usage_reports")): report = False install_info = InstallInfo.load() logging.info("Cleaning up leftovers(if any)") cleanup_postinstall_files() logging.info("Ejecting installation CDs") eject_cd("vm-0", FIRST_CDROM_DRIVE) if install_info.enable_unattended: # If unattended install is enabled, we have an additional CD-ROM drive eject_cd("vm-0", SECOND_CDROM_DRIVE) output = subprocess.check_output(['vmi-win-guid', 'name', 'vm-0'], timeout=30).decode('utf-8') try: version = re.search(r'Version: (.*)', output).group(1) pdb = re.search(r'PDB GUID: ([0-9a-f]+)', output).group(1) fn = re.search(r'Kernel filename: ([a-z]+\.[a-z]+)', output).group(1) except AttributeError: logging.error("Failed to obtain kernel PDB GUID/Kernel filename.") return logging.info("Determined PDB GUID: {}".format(pdb)) logging.info("Determined kernel filename: {}".format(fn)) logging.info("Fetching PDB file...") dest = fetch_pdb(fn, pdb, destdir=PROFILE_DIR) logging.info("Generating profile out of PDB file...") profile = make_pdb_profile(dest) logging.info("Saving profile...") kernel_profile = os.path.join(PROFILE_DIR, 'kernel.json') with open(kernel_profile, 'w') as f: f.write(profile) vmi_offsets = extract_vmi_offsets('vm-0', kernel_profile) explorer_pid = extract_explorer_pid('vm-0', kernel_profile, vmi_offsets) runtime_info = RuntimeInfo(vmi_offsets=vmi_offsets, inject_pid=explorer_pid) logging.info("Saving runtime profile...") with open(os.path.join(PROFILE_DIR, 'runtime.json'), 'w') as f: f.write(runtime_info.to_json(indent=4)) logging.info("Saving VM snapshot...") subprocess.check_output('xl save vm-0 ' + os.path.join(VOLUME_DIR, "snapshot.sav"), shell=True) storage_backend = get_storage_backend(install_info) storage_backend.snapshot_vm0_volume() logging.info("Snapshot was saved succesfully.") if generate_usermode: try: create_rekall_profiles(install_info) except RuntimeError as e: logging.warning("Generating usermode profiles failed") logging.exception(e) if report: send_usage_report({ "kernel": { "guid": pdb, "filename": fn, "version": version }, "install_iso": { "sha256": install_info.iso_sha256 } }) logging.info("All right, drakrun setup is done.") logging.info("First instance of drakrun will be enabled automatically...") subprocess.check_output('systemctl enable drakrun@1', shell=True) subprocess.check_output('systemctl start drakrun@1', shell=True) logging.info("If you want to have more parallel instances, execute:") logging.info(" # draksetup scale <number of instances>")
def install(vcpus, memory, storage_backend, disk_size, iso_path, zfs_tank_name, lvm_volume_group, unattended_xml): if not check_root(): return if storage_backend == "lvm" and lvm_volume_group is None: raise Exception("lvm storage backend requires --lvm-volume-group") if storage_backend == "zfs" and zfs_tank_name is None: raise Exception("zfs storage backend requires --zfs-tank-name") if not sanity_check(): logging.error("Sanity check failed.") return stop_all_drakruns() logging.info("Performing installation...") if vcpus < 1: logging.error("Your VM must have at least 1 vCPU.") return if memory < 512: logging.error("Your VM must have at least 512 MB RAM.") return if memory < 1536: logging.warning( "Using less than 1.5 GB RAM per VM is not recommended for any supported system." ) if unattended_xml: logging.info("Baking unattended.iso for automated installation") with tempfile.TemporaryDirectory() as tmpdir: tmp_xml_path = os.path.join(tmpdir, 'autounattend.xml') with open(tmp_xml_path, 'wb') as fw: with open(unattended_xml, 'rb') as fr: fw.write(fr.read()) try: subprocess.check_output([ 'genisoimage', '-o', os.path.join(VOLUME_DIR, "unattended.iso"), '-J', '-r', tmp_xml_path ], stderr=subprocess.STDOUT) except subprocess.CalledProcessError: logging.exception("Failed to generate unattended.iso.") sha256_hash = hashlib.sha256() logging.info("Calculating hash of iso") iso_file_size = os.stat(iso_path).st_size block_size = 128 * 1024 with tqdm(total=iso_file_size, unit_scale=True) as pbar: with open(iso_path, "rb") as f: for byte_block in iter(lambda: f.read(block_size), b""): pbar.update(block_size) sha256_hash.update(byte_block) iso_sha256 = sha256_hash.hexdigest() install_info = InstallInfo(vcpus=vcpus, memory=memory, storage_backend=storage_backend, disk_size=disk_size, iso_path=os.path.abspath(iso_path), zfs_tank_name=zfs_tank_name, lvm_volume_group=lvm_volume_group, enable_unattended=unattended_xml is not None, iso_sha256=iso_sha256) install_info.save() try: subprocess.check_output('xl uptime vm-0', shell=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError: pass else: logging.info('Detected that vm-0 is already running, stopping it.') subprocess.run('xl destroy vm-0', shell=True, check=True) generate_vm_conf(install_info, 0) backend = get_storage_backend(install_info) backend.initialize_vm0_volume(disk_size) try: subprocess.check_output("brctl show", shell=True) except subprocess.CalledProcessError: logging.exception( "Failed to execute brctl show. Make sure you have bridge-utils installed." ) return net_enable = conf['drakrun'].getboolean('net_enable', fallback=False) out_interface = conf['drakrun'].get('out_interface', '') dns_server = conf['drakrun'].get('dns_server', '') setup_vm_network(vm_id=0, net_enable=net_enable, out_interface=out_interface, dns_server=dns_server) if net_enable: start_dnsmasq(vm_id=0, dns_server=dns_server, background=True) cfg_path = os.path.join(VM_CONFIG_DIR, "vm-0.cfg") try: subprocess.run('xl create {}'.format(shlex.quote(cfg_path)), shell=True, check=True) except subprocess.CalledProcessError: logging.exception("Failed to launch VM vm-0") return logging.info("-" * 80) logging.info("Initial VM setup is complete and the vm-0 was launched.") logging.info( "Please now VNC to the port 5900 on this machine to perform Windows installation." ) logging.info( "After you have installed Windows and booted it to the desktop, please execute:" ) logging.info("# draksetup postinstall") with open(cfg_path, "r") as f: data = f.read() m = re.search(r'vncpasswd[ ]*=(.*)', data) if m: passwd = m.group(1).strip() if passwd[0] == '"' and passwd[-1] == '"': passwd = passwd[1:-1] logging.info("Your configured VNC password is:") logging.info(passwd) logging.info( "Please note that on some machines, system installer may boot for up to 10 minutes" ) logging.info( "and may look unresponsive during the process. Please be patient.") logging.info("-" * 80)