VMEncryption/main/handle.py (1,500 lines of code) (raw):

#!/usr/bin/env python # # Azure Disk Encryption For Linux Extension # # Copyright 2019 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import filecmp import json import os import os.path import re import subprocess import sys import time import tempfile import traceback import uuid import shutil from Utils import HandlerUtil from Common import CommonVariables, CryptItem from ExtensionParameter import ExtensionParameter from DiskUtil import DiskUtil from ResourceDiskUtil import ResourceDiskUtil from BackupLogger import BackupLogger from KeyVaultUtil import KeyVaultUtil from EncryptionConfig import EncryptionConfig from patch import GetDistroPatcher from BekUtil import BekUtil from check_util import CheckUtil from DecryptionMarkConfig import DecryptionMarkConfig from EncryptionMarkConfig import EncryptionMarkConfig from EncryptionEnvironment import EncryptionEnvironment from OnGoingItemConfig import OnGoingItemConfig from ProcessLock import ProcessLock from CommandExecutor import CommandExecutor, ProcessCommunicator from __builtin__ import int def install(): hutil.do_parse_context('Install') hutil.do_exit(0, 'Install', CommonVariables.extension_success_status, str(CommonVariables.success), 'Install Succeeded') def disable(): hutil.do_parse_context('Disable') # Archive configs at disable to make them available to new extension version prior to update # The extension update handshake is [old:disable][new:update][old:uninstall][new:install] hutil.archive_old_configs() hutil.do_exit(0, 'Disable', CommonVariables.extension_success_status, '0', 'Disable succeeded') def uninstall(): hutil.do_parse_context('Uninstall') hutil.do_exit(0, 'Uninstall', CommonVariables.extension_success_status, '0', 'Uninstall succeeded') def disable_encryption(): hutil.do_parse_context('DisableEncryption') logger.log('Disabling encryption') decryption_marker = DecryptionMarkConfig(logger, encryption_environment) if decryption_marker.config_file_exists(): logger.log(msg="decryption is marked, starting daemon.", level=CommonVariables.InfoLevel) start_daemon('DisableEncryption') hutil.do_exit(exit_code=0, operation='DisableEncryption', status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message='Decryption started') exit_status = { 'operation': 'DisableEncryption', 'status': CommonVariables.extension_success_status, 'status_code': str(CommonVariables.success), 'message': 'Decryption completed' } hutil.exit_if_same_seq(exit_status) hutil.save_seq() try: extension_parameter = ExtensionParameter(hutil, logger, DistroPatcher, encryption_environment, get_protected_settings(), get_public_settings()) disk_util = DiskUtil(hutil=hutil, patching=DistroPatcher, logger=logger, encryption_environment=encryption_environment) encryption_status = json.loads(disk_util.get_encryption_status()) if encryption_status["os"] != "NotEncrypted": raise Exception("Disabling encryption is not supported when OS volume is encrypted") bek_util = BekUtil(disk_util, logger) encryption_config = EncryptionConfig(encryption_environment, logger) bek_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) crypt_items = disk_util.get_crypt_items() logger.log('Found {0} items to decrypt'.format(len(crypt_items))) for crypt_item in crypt_items: disk_util.create_cleartext_key(crypt_item.mapper_name) add_result = disk_util.luks_add_cleartext_key(bek_passphrase_file, crypt_item.dev_path, crypt_item.mapper_name, crypt_item.luks_header_path) if add_result != CommonVariables.process_success: if disk_util.is_luks_device(crypt_item.dev_path, crypt_item.luks_header_path): raise Exception("luksAdd failed with return code {0}".format(add_result)) else: logger.log("luksAdd failed with return code {0}".format(add_result)) logger.log("Ignoring for now, as device ({0}) does not seem to be a luks device".format(crypt_item.dev_path)) continue if crypt_item.dev_path.startswith("/dev/sd"): logger.log('Updating crypt item entry to use mapper name') logger.log('Device name before update: {0}'.format(crypt_item.dev_path)) crypt_item.dev_path = disk_util.get_persistent_path_by_sdx_path(crypt_item.dev_path) logger.log('Device name after update: {0}'.format(crypt_item.dev_path)) crypt_item.uses_cleartext_key = True disk_util.update_crypt_item(crypt_item, None) logger.log('Added cleartext key for {0}'.format(crypt_item)) decryption_marker.command = extension_parameter.command decryption_marker.volume_type = extension_parameter.VolumeType decryption_marker.commit() hutil.do_exit(exit_code=0, operation='DisableEncryption', status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message='Decryption started') except Exception as e: message = "Failed to disable the extension with error: {0}, stack trace: {1}".format(e, traceback.format_exc()) logger.log(msg=message, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.unknown_error, operation='DisableEncryption', status=CommonVariables.extension_error_status, code=str(CommonVariables.unknown_error), message=message) def get_public_settings(): public_settings_str = hutil._context._config['runtimeSettings'][0]['handlerSettings'].get('publicSettings') if isinstance(public_settings_str, basestring): return json.loads(public_settings_str) else: return public_settings_str def get_protected_settings(): protected_settings_str = hutil._context._config['runtimeSettings'][0]['handlerSettings'].get('protectedSettings') if isinstance(protected_settings_str, basestring): return json.loads(protected_settings_str) else: return protected_settings_str def update_encryption_settings(): hutil.do_parse_context('UpdateEncryptionSettings') logger.log('Updating encryption settings') # re-install extra packages like cryptsetup if no longer on system from earlier enable try: DistroPatcher.install_extras() except Exception as e: message = "Failed to update encryption settings with error: {0}, stack trace: {1}".format(e, traceback.format_exc()) hutil.do_exit(exit_code=CommonVariables.missing_dependency, operation='UpdateEncryptionSettings', status=CommonVariables.extension_error_status, code=str(CommonVariables.missing_dependency), message=message) encryption_config = EncryptionConfig(encryption_environment, logger) config_secret_seq = encryption_config.get_secret_seq_num() current_secret_seq_num = int(config_secret_seq if config_secret_seq else -1) update_call_seq_num = hutil.get_current_seq() logger.log("Current secret was created in operation #{0}".format(current_secret_seq_num)) logger.log("The update call is operation #{0}".format(update_call_seq_num)) executor = CommandExecutor(logger) executor.Execute("mount /boot") try: disk_util = DiskUtil(hutil=hutil, patching=DistroPatcher, logger=logger, encryption_environment=encryption_environment) bek_util = BekUtil(disk_util, logger) extension_parameter = ExtensionParameter(hutil, logger, DistroPatcher, encryption_environment, get_protected_settings(), get_public_settings()) existing_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) if current_secret_seq_num < update_call_seq_num: if extension_parameter.passphrase is None or extension_parameter.passphrase == "": extension_parameter.passphrase = bek_util.generate_passphrase(extension_parameter.KeyEncryptionAlgorithm) logger.log('Recreating secret to store in the KeyVault') keyVaultUtil = KeyVaultUtil(logger) temp_keyfile = tempfile.NamedTemporaryFile(delete=False) temp_keyfile.write(extension_parameter.passphrase) temp_keyfile.close() for crypt_item in disk_util.get_crypt_items(): if not crypt_item: continue before_keyslots = disk_util.luks_dump_keyslots(crypt_item.dev_path, crypt_item.luks_header_path) logger.log("Before key addition, keyslots for {0}: {1}".format(crypt_item.dev_path, before_keyslots)) logger.log("Adding new key for {0}".format(crypt_item.dev_path)) luks_add_result = disk_util.luks_add_key(passphrase_file=existing_passphrase_file, dev_path=crypt_item.dev_path, mapper_name=crypt_item.mapper_name, header_file=crypt_item.luks_header_path, new_key_path=temp_keyfile.name) logger.log("luks add result is {0}".format(luks_add_result)) after_keyslots = disk_util.luks_dump_keyslots(crypt_item.dev_path, crypt_item.luks_header_path) logger.log("After key addition, keyslots for {0}: {1}".format(crypt_item.dev_path, after_keyslots)) new_keyslot = list(map(lambda x: x[0] != x[1], zip(before_keyslots, after_keyslots))).index(True) logger.log("New key was added in keyslot {0}".format(new_keyslot)) # crypt_item.current_luks_slot = new_keyslot # disk_util.update_crypt_item(crypt_item) logger.log("New key successfully added to all encrypted devices") if DistroPatcher.distro_info[0] == "Ubuntu": logger.log("Updating initrd image with new osluksheader.") executor.Execute("update-initramfs -u -k all", True) if DistroPatcher.distro_info[0] == "redhat" or DistroPatcher.distro_info[0] == "centos": distro_version = DistroPatcher.distro_info[1] if distro_version.startswith('7.'): logger.log("Updating initrd image with new osluksheader.") executor.ExecuteInBash("/usr/sbin/dracut -f -v --kver `grubby --default-kernel | sed 's|/boot/vmlinuz-||g'`", True) os.unlink(temp_keyfile.name) # install Python ADAL support if using client certificate authentication if extension_parameter.AADClientCertThumbprint: DistroPatcher.install_adal() kek_secret_id_created = keyVaultUtil.create_kek_secret(Passphrase=extension_parameter.passphrase, KeyVaultURL=extension_parameter.KeyVaultURL, KeyEncryptionKeyURL=extension_parameter.KeyEncryptionKeyURL, AADClientID=extension_parameter.AADClientID, AADClientCertThumbprint=extension_parameter.AADClientCertThumbprint, KeyEncryptionAlgorithm=extension_parameter.KeyEncryptionAlgorithm, AADClientSecret=extension_parameter.AADClientSecret, DiskEncryptionKeyFileName=extension_parameter.DiskEncryptionKeyFileName) if kek_secret_id_created is None: hutil.do_exit(exit_code=CommonVariables.create_encryption_secret_failed, operation='UpdateEncryptionSettings', status=CommonVariables.extension_error_status, code=str(CommonVariables.create_encryption_secret_failed), message='UpdateEncryptionSettings failed.') else: encryption_config.passphrase_file_name = extension_parameter.DiskEncryptionKeyFileName encryption_config.secret_id = kek_secret_id_created encryption_config.secret_seq_num = hutil.get_current_seq() encryption_config.commit() shutil.copy(existing_passphrase_file, encryption_environment.bek_backup_path) logger.log("Backed up BEK at {0}".format(encryption_environment.bek_backup_path)) hutil.do_exit(exit_code=0, operation='UpdateEncryptionSettings', status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message=str(kek_secret_id_created)) else: logger.log('Secret has already been updated') mount_encrypted_disks(disk_util, bek_util, existing_passphrase_file, encryption_config) disk_util.log_lsblk_output() hutil.exit_if_same_seq() # remount bek volume existing_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) if extension_parameter.passphrase and extension_parameter.passphrase != file(existing_passphrase_file).read(): logger.log("The new passphrase has not been placed in BEK volume yet") logger.log("Skipping removal of old passphrase") exit_without_status_report() logger.log('Removing old passphrase') for crypt_item in disk_util.get_crypt_items(): if not crypt_item: continue if filecmp.cmp(existing_passphrase_file, encryption_environment.bek_backup_path): logger.log('Current BEK and backup are the same, skipping removal') continue logger.log('Removing old passphrase from {0}'.format(crypt_item.dev_path)) keyslots = disk_util.luks_dump_keyslots(crypt_item.dev_path, crypt_item.luks_header_path) logger.log("Keyslots before removal: {0}".format(keyslots)) luks_remove_result = disk_util.luks_remove_key(passphrase_file=encryption_environment.bek_backup_path, dev_path=crypt_item.dev_path, header_file=crypt_item.luks_header_path) logger.log("luks remove result is {0}".format(luks_remove_result)) keyslots = disk_util.luks_dump_keyslots(crypt_item.dev_path, crypt_item.luks_header_path) logger.log("Keyslots after removal: {0}".format(keyslots)) logger.log("Old key successfully removed from all encrypted devices") if DistroPatcher.distro_info[0] == "Ubuntu": logger.log("Updating initrd image with new osluksheader.") executor.Execute("update-initramfs -u -k all", True) if DistroPatcher.distro_info[0] == "redhat" or DistroPatcher.distro_info[0] == "centos": distro_version = DistroPatcher.distro_info[1] if distro_version.startswith('7.'): logger.log("Updating initrd image with new osluksheader.") executor.ExecuteInBash("/usr/sbin/dracut -f -v --kver `grubby --default-kernel | sed 's|/boot/vmlinuz-||g'`", True) hutil.save_seq() extension_parameter.commit() os.unlink(encryption_environment.bek_backup_path) hutil.do_exit(exit_code=0, operation='UpdateEncryptionSettings', status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message='Encryption settings updated') except Exception as e: message = "Failed to update encryption settings with error: {0}, stack trace: {1}".format(e, traceback.format_exc()) logger.log(msg=message, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.unknown_error, operation='UpdateEncryptionSettings', status=CommonVariables.extension_error_status, code=str(CommonVariables.unknown_error), message=message) def update(): # The extension update handshake is [old:disable][new:update][old:uninstall][new:install] # this method is called when updating an older version of the extension to a newer version hutil.do_parse_context('Update') logger.log("Installing pre-requisites") DistroPatcher.install_extras() DistroPatcher.update_prereq() hutil.do_exit(0, 'Update', CommonVariables.extension_success_status, '0', 'Update Succeeded') def exit_without_status_report(): sys.exit(0) def not_support_header_option_distro(patching): if patching.distro_info[0].lower() == "centos" and patching.distro_info[1].startswith('6.'): return True if patching.distro_info[0].lower() == "redhat" and patching.distro_info[1].startswith('6.'): return True if patching.distro_info[0].lower() == "suse" and patching.distro_info[1].startswith('11'): return True return False def none_or_empty(obj): if obj is None or obj == "": return True else: return False def toggle_se_linux_for_centos7(disable): if DistroPatcher.distro_info[0].lower() == 'centos' and DistroPatcher.distro_info[1].startswith('7.0'): if disable: se_linux_status = encryption_environment.get_se_linux() if se_linux_status.lower() == 'enforcing': encryption_environment.disable_se_linux() return True else: encryption_environment.enable_se_linux() return False def mount_encrypted_disks(disk_util, bek_util, passphrase_file, encryption_config): # mount encrypted resource disk volume_type = encryption_config.get_volume_type().lower() if volume_type == CommonVariables.VolumeTypeData.lower() or volume_type == CommonVariables.VolumeTypeAll.lower(): resource_disk_util = ResourceDiskUtil(logger, disk_util, passphrase_file, get_public_settings(), DistroPatcher.distro_info) resource_disk_util.automount() logger.log("mounted encrypted resource disk") # add walkaround for the centos 7.0 se_linux_status = None if DistroPatcher.distro_info[0].lower() == 'centos' and DistroPatcher.distro_info[1].startswith('7.0'): se_linux_status = encryption_environment.get_se_linux() if se_linux_status.lower() == 'enforcing': encryption_environment.disable_se_linux() # mount any data disks - make sure the azure disk config path exists. for crypt_item in disk_util.get_crypt_items(): if not crypt_item: continue if not os.path.exists(os.path.join(CommonVariables.dev_mapper_root, crypt_item.mapper_name)): luks_open_result = disk_util.luks_open(passphrase_file=passphrase_file, dev_path=crypt_item.dev_path, mapper_name=crypt_item.mapper_name, header_file=crypt_item.luks_header_path, uses_cleartext_key=crypt_item.uses_cleartext_key) logger.log("luks open result is {0}".format(luks_open_result)) disk_util.mount_crypt_item(crypt_item, passphrase_file) if DistroPatcher.distro_info[0].lower() == 'centos' and DistroPatcher.distro_info[1].startswith('7.0'): if se_linux_status is not None and se_linux_status.lower() == 'enforcing': encryption_environment.enable_se_linux() def main(): global hutil, DistroPatcher, logger, encryption_environment HandlerUtil.LoggerInit('/var/log/waagent.log', '/dev/stdout') HandlerUtil.waagent.Log("{0} started to handle.".format(CommonVariables.extension_name)) hutil = HandlerUtil.HandlerUtility(HandlerUtil.waagent.Log, HandlerUtil.waagent.Error, CommonVariables.extension_name) logger = BackupLogger(hutil) DistroPatcher = GetDistroPatcher(logger) hutil.patching = DistroPatcher encryption_environment = EncryptionEnvironment(patching=DistroPatcher, logger=logger) disk_util = DiskUtil(hutil=hutil, patching=DistroPatcher, logger=logger, encryption_environment=encryption_environment) hutil.disk_util = disk_util if DistroPatcher is None: hutil.do_exit(exit_code=CommonVariables.os_not_supported, operation='Enable', status=CommonVariables.extension_error_status, code=(CommonVariables.os_not_supported), message='Enable failed: the os is not supported') for a in sys.argv[1:]: if re.match("^([-/]*)(disable)", a): disable() elif re.match("^([-/]*)(uninstall)", a): uninstall() elif re.match("^([-/]*)(install)", a): install() elif re.match("^([-/]*)(enable)", a): enable() elif re.match("^([-/]*)(update)", a): update() elif re.match("^([-/]*)(daemon)", a): daemon() def mark_encryption(command, volume_type, disk_format_query): encryption_marker = EncryptionMarkConfig(logger, encryption_environment) encryption_marker.command = command encryption_marker.volume_type = volume_type encryption_marker.diskFormatQuery = disk_format_query encryption_marker.commit() return encryption_marker def is_daemon_running(): handler_path = os.path.join(os.getcwd(), __file__) daemon_arg = "-daemon" psproc = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE) pslist, _ = psproc.communicate() for line in pslist.split("\n"): if handler_path in line and daemon_arg in line: return True return False def enable(): while True: hutil.do_parse_context('Enable') logger.log('Enabling extension') public_settings = get_public_settings() logger.log('Public settings:\n{0}'.format(json.dumps(public_settings, sort_keys=True, indent=4))) cutil = CheckUtil(logger) # Mount already encrypted disks before running fatal prechecks disk_util = DiskUtil(hutil=hutil, patching=DistroPatcher, logger=logger, encryption_environment=encryption_environment) bek_util = BekUtil(disk_util, logger) existing_passphrase_file = None encryption_config = EncryptionConfig(encryption_environment=encryption_environment, logger=logger) existing_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) if existing_passphrase_file is not None: mount_encrypted_disks(disk_util=disk_util, bek_util=bek_util, encryption_config=encryption_config, passphrase_file=existing_passphrase_file) # Migrate to early unlock if using crypt mount if disk_util.should_use_azure_crypt_mount(): disk_util.migrate_crypt_items(existing_passphrase_file) encryption_status = json.loads(disk_util.get_encryption_status()) # run fatal prechecks, report error if exceptions are caught try: cutil.precheck_for_fatal_failures(public_settings, encryption_status, DistroPatcher) except Exception as e: logger.log("PRECHECK: Fatal Exception thrown during precheck") logger.log(traceback.format_exc()) msg = e.message hutil.do_exit(exit_code=CommonVariables.configuration_error, operation='Enable', status=CommonVariables.extension_error_status, code=(CommonVariables.configuration_error), message=msg) hutil.disk_util.log_lsblk_output() # run prechecks and log any failures detected try: if cutil.is_non_fatal_precheck_failure(): logger.log("PRECHECK: Precheck failure, incompatible environment suspected") else: logger.log("PRECHECK: Prechecks successful") except Exception: logger.log("PRECHECK: Exception thrown during precheck") logger.log(traceback.format_exc()) encryption_operation = public_settings.get(CommonVariables.EncryptionEncryptionOperationKey) if encryption_operation in [CommonVariables.EnableEncryption, CommonVariables.EnableEncryptionFormat, CommonVariables.EnableEncryptionFormatAll]: logger.log("handle.py found enable encryption operation") extension_parameter = ExtensionParameter(hutil, logger, DistroPatcher, encryption_environment, get_protected_settings(), public_settings) if os.path.exists(encryption_environment.bek_backup_path) or (extension_parameter.config_file_exists() and extension_parameter.config_changed()): logger.log("Config has changed, updating encryption settings") update_encryption_settings() extension_parameter.commit() else: logger.log("Config did not change or first call, enabling encryption") enable_encryption() elif encryption_operation == CommonVariables.DisableEncryption: logger.log("handle.py found disable encryption operation") disable_encryption() elif encryption_operation == CommonVariables.QueryEncryptionStatus: logger.log("handle.py found query operation") encryption_marker = EncryptionMarkConfig(logger, encryption_environment) if is_daemon_running() or (encryption_marker and not encryption_marker.config_file_exists()): logger.log("A daemon is already running or no operation in progress, exiting without status report") hutil.redo_last_status() exit_without_status_report() else: logger.log("No daemon found, trying to find the last non-query operation") hutil.find_last_nonquery_operation = True else: msg = "Encryption operation {0} is not supported".format(encryption_operation) logger.log(msg) hutil.do_exit(exit_code=CommonVariables.configuration_error, operation='Enable', status=CommonVariables.extension_error_status, code=(CommonVariables.configuration_error), message=msg) def enable_encryption(): hutil.do_parse_context('EnableEncryption') # we need to start another subprocess to do it, because the initial process # would be killed by the wala in 5 minutes. logger.log('Enabling encryption') """ trying to mount the crypted items. """ disk_util = DiskUtil(hutil=hutil, patching=DistroPatcher, logger=logger, encryption_environment=encryption_environment) bek_util = BekUtil(disk_util, logger) existing_passphrase_file = None encryption_config = EncryptionConfig(encryption_environment=encryption_environment, logger=logger) config_path_result = disk_util.make_sure_path_exists(encryption_environment.encryption_config_path) if config_path_result != CommonVariables.process_success: logger.log(msg="azure encryption path creation failed.", level=CommonVariables.ErrorLevel) if encryption_config.config_file_exists(): existing_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) if existing_passphrase_file is not None: mount_encrypted_disks(disk_util=disk_util, bek_util=bek_util, encryption_config=encryption_config, passphrase_file=existing_passphrase_file) else: logger.log(msg="EncryptionConfig is present, but could not get the BEK file.", level=CommonVariables.WarningLevel) hutil.redo_last_status() exit_without_status_report() ps = subprocess.Popen(["ps", "aux"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) ps_stdout, ps_stderr = ps.communicate() if re.search(r"dd.*of=/dev/mapper/osencrypt", ps_stdout): logger.log(msg="OS disk encryption already in progress, exiting", level=CommonVariables.WarningLevel) exit_without_status_report() # handle the re-call scenario. the re-call would resume? # if there's one tag for the next reboot. encryption_marker = EncryptionMarkConfig(logger, encryption_environment) try: protected_settings_str = hutil._context._config['runtimeSettings'][0]['handlerSettings'].get('protectedSettings') public_settings_str = hutil._context._config['runtimeSettings'][0]['handlerSettings'].get('publicSettings') if isinstance(public_settings_str, basestring): public_settings = json.loads(public_settings_str) else: public_settings = public_settings_str if isinstance(protected_settings_str, basestring): protected_settings = json.loads(protected_settings_str) else: protected_settings = protected_settings_str extension_parameter = ExtensionParameter(hutil, logger, DistroPatcher, encryption_environment, protected_settings, public_settings) kek_secret_id_created = None encryption_marker = EncryptionMarkConfig(logger, encryption_environment) if encryption_marker.config_file_exists(): # verify the encryption mark logger.log(msg="encryption mark is there, starting daemon.", level=CommonVariables.InfoLevel) start_daemon('EnableEncryption') else: encryption_config = EncryptionConfig(encryption_environment, logger) exit_status = None if encryption_config.config_file_exists(): exit_status = { 'operation': 'EnableEncryption', 'status': CommonVariables.extension_success_status, 'status_code': str(CommonVariables.success), 'message': encryption_config.get_secret_id() } hutil.exit_if_same_seq(exit_status) hutil.save_seq() encryption_config.volume_type = extension_parameter.VolumeType encryption_config.commit() if encryption_config.config_file_exists() and existing_passphrase_file is not None: logger.log(msg="config file exists and passphrase file exists.", level=CommonVariables.WarningLevel) encryption_marker = mark_encryption(command=extension_parameter.command, volume_type=extension_parameter.VolumeType, disk_format_query=extension_parameter.DiskFormatQuery) start_daemon('EnableEncryption') else: """ creating the secret, the secret would be transferred to a bek volume after the updatevm called in powershell. """ # store the luks passphrase in the secret. keyVaultUtil = KeyVaultUtil(logger) """ validate the parameters """ if(extension_parameter.VolumeType is None or not any([extension_parameter.VolumeType.lower() == vt.lower() for vt in CommonVariables.SupportedVolumeTypes])): if encryption_config.config_file_exists(): existing_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) if existing_passphrase_file is None: logger.log("Unsupported volume type specified and BEK volume does not exist, clearing encryption config") encryption_config.clear_config() hutil.do_exit(exit_code=CommonVariables.configuration_error, operation='EnableEncryption', status=CommonVariables.extension_error_status, code=str(CommonVariables.configuration_error), message='VolumeType "{0}" is not supported'.format(extension_parameter.VolumeType)) if extension_parameter.command not in [CommonVariables.EnableEncryption, CommonVariables.EnableEncryptionFormat, CommonVariables.EnableEncryptionFormatAll]: hutil.do_exit(exit_code=CommonVariables.configuration_error, operation='EnableEncryption', status=CommonVariables.extension_error_status, code=str(CommonVariables.configuration_error), message='Command "{0}" is not supported'.format(extension_parameter.command)) """ this is the fresh call case """ # handle the passphrase related if existing_passphrase_file is None: if extension_parameter.passphrase is None or extension_parameter.passphrase == "": extension_parameter.passphrase = bek_util.generate_passphrase(extension_parameter.KeyEncryptionAlgorithm) else: logger.log(msg="the extension_parameter.passphrase is already defined") # install Python ADAL support if using client certificate authentication if extension_parameter.AADClientCertThumbprint: DistroPatcher.install_adal() kek_secret_id_created = keyVaultUtil.create_kek_secret(Passphrase=extension_parameter.passphrase, KeyVaultURL=extension_parameter.KeyVaultURL, KeyEncryptionKeyURL=extension_parameter.KeyEncryptionKeyURL, AADClientID=extension_parameter.AADClientID, AADClientCertThumbprint=extension_parameter.AADClientCertThumbprint, KeyEncryptionAlgorithm=extension_parameter.KeyEncryptionAlgorithm, AADClientSecret=extension_parameter.AADClientSecret, DiskEncryptionKeyFileName=extension_parameter.DiskEncryptionKeyFileName) if kek_secret_id_created is None: encryption_config.clear_config() hutil.do_exit(exit_code=CommonVariables.create_encryption_secret_failed, operation='EnableEncryption', status=CommonVariables.extension_error_status, code=str(CommonVariables.create_encryption_secret_failed), message='Enable failed.') else: encryption_config.passphrase_file_name = extension_parameter.DiskEncryptionKeyFileName encryption_config.volume_type = extension_parameter.VolumeType encryption_config.secret_id = kek_secret_id_created encryption_config.secret_seq_num = hutil.get_current_seq() encryption_config.commit() extension_parameter.commit() encryption_marker = mark_encryption(command=extension_parameter.command, volume_type=extension_parameter.VolumeType, disk_format_query=extension_parameter.DiskFormatQuery) if kek_secret_id_created: hutil.do_exit(exit_code=0, operation='EnableEncryption', status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message=str(kek_secret_id_created)) else: """ the enabling called again. the passphrase would be re-used. """ hutil.do_exit(exit_code=0, operation='EnableEncryption', status=CommonVariables.extension_success_status, code=str(CommonVariables.encrypttion_already_enabled), message=str(kek_secret_id_created)) except Exception as e: message = "Failed to enable the extension with error: {0}, stack trace: {1}".format(e, traceback.format_exc()) logger.log(msg=message, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.unknown_error, operation='EnableEncryption', status=CommonVariables.extension_error_status, code=str(CommonVariables.unknown_error), message=message) def enable_encryption_format(passphrase, disk_format_query, disk_util, force=False): logger.log('enable_encryption_format') logger.log('disk format query is {0}'.format(disk_format_query)) json_parsed = json.loads(disk_format_query) if type(json_parsed) is dict: encryption_format_items = [json_parsed, ] elif type(json_parsed) is list: encryption_format_items = json_parsed else: raise Exception("JSON parse error. Input: {0}".format(disk_format_query)) for encryption_item in encryption_format_items: dev_path_in_query = None if "scsi" in encryption_item and encryption_item["scsi"] != '': dev_path_in_query = disk_util.query_dev_sdx_path_by_scsi_id(encryption_item["scsi"]) if "dev_path" in encryption_item and encryption_item["dev_path"] != '': dev_path_in_query = encryption_item["dev_path"] if not dev_path_in_query: raise Exception("Could not find a device path for Encryption Item: {0}".format(json.dumps(encryption_item))) devices = disk_util.get_device_items(dev_path_in_query) if len(devices) != 1: logger.log(msg=("the device with this path {0} have more than one sub device. so skip it.".format(dev_path_in_query)), level=CommonVariables.WarningLevel) continue else: device_item = devices[0] if device_item.file_system is None or device_item.file_system == "" or force: if device_item.mount_point: disk_util.swapoff() disk_util.umount(device_item.mount_point) mapper_name = str(uuid.uuid4()) logger.log("encrypting " + str(device_item)) encrypted_device_path = os.path.join(CommonVariables.dev_mapper_root, mapper_name) try: se_linux_status = None if DistroPatcher.distro_info[0].lower() == 'centos' and DistroPatcher.distro_info[1].startswith('7.0'): se_linux_status = encryption_environment.get_se_linux() if se_linux_status.lower() == 'enforcing': encryption_environment.disable_se_linux() encrypt_result = disk_util.encrypt_disk(dev_path=dev_path_in_query, passphrase_file=passphrase, mapper_name=mapper_name, header_file=None) finally: if DistroPatcher.distro_info[0].lower() == 'centos' and DistroPatcher.distro_info[1].startswith('7.0'): if se_linux_status is not None and se_linux_status.lower() == 'enforcing': encryption_environment.enable_se_linux() if encrypt_result == CommonVariables.process_success: # TODO: let customer specify the default file system in the # parameter file_system = None if "file_system" in encryption_item and encryption_item["file_system"] != "": file_system = encryption_item["file_system"] else: file_system = CommonVariables.default_file_system format_disk_result = disk_util.format_disk(dev_path=encrypted_device_path, file_system=file_system) if format_disk_result != CommonVariables.process_success: logger.log(msg=("format of disk {0} failed with result: {1}".format(encrypted_device_path, format_disk_result)), level=CommonVariables.ErrorLevel) crypt_item_to_update = CryptItem() crypt_item_to_update.mapper_name = mapper_name crypt_item_to_update.dev_path = dev_path_in_query crypt_item_to_update.luks_header_path = None crypt_item_to_update.file_system = file_system crypt_item_to_update.uses_cleartext_key = False crypt_item_to_update.current_luks_slot = 0 if "name" in encryption_item and encryption_item["name"] != "": crypt_item_to_update.mount_point = os.path.join("/mnt/", str(encryption_item["name"])) else: crypt_item_to_update.mount_point = os.path.join("/mnt/", mapper_name) # allow override through the new full_mount_point field if "full_mount_point" in encryption_item and encryption_item["full_mount_point"] != "": crypt_item_to_update.mount_point = os.path.join(str(encryption_item["full_mount_point"])) logger.log(msg="modifying/removing the entry for unencrypted drive in fstab", level=CommonVariables.InfoLevel) disk_util.modify_fstab_entry_encrypt(crypt_item_to_update.mount_point, os.path.join(CommonVariables.dev_mapper_root, mapper_name)) disk_util.make_sure_path_exists(crypt_item_to_update.mount_point) update_crypt_item_result = disk_util.add_crypt_item(crypt_item_to_update, passphrase) if not update_crypt_item_result: logger.log(msg="update crypt item failed", level=CommonVariables.ErrorLevel) mount_result = disk_util.mount_filesystem(dev_path=encrypted_device_path, mount_point=crypt_item_to_update.mount_point) logger.log(msg=("mount result is {0}".format(mount_result))) else: logger.log(msg="encryption failed with code {0}".format(encrypt_result), level=CommonVariables.ErrorLevel) else: logger.log(msg=("the item fstype is not empty {0}".format(device_item.file_system))) def encrypt_inplace_without_seperate_header_file(passphrase_file, device_item, disk_util, bek_util, status_prefix='', ongoing_item_config=None): """ if ongoing_item_config is not None, then this is a resume case. this function will return the phase """ logger.log("encrypt_inplace_without_seperate_header_file") current_phase = CommonVariables.EncryptionPhaseBackupHeader if ongoing_item_config is None: ongoing_item_config = OnGoingItemConfig(encryption_environment=encryption_environment, logger=logger) ongoing_item_config.current_block_size = CommonVariables.default_block_size ongoing_item_config.current_slice_index = 0 ongoing_item_config.device_size = device_item.size ongoing_item_config.file_system = device_item.file_system ongoing_item_config.luks_header_file_path = None ongoing_item_config.mapper_name = str(uuid.uuid4()) ongoing_item_config.mount_point = device_item.mount_point if os.path.exists(os.path.join('/dev/', device_item.name)): ongoing_item_config.original_dev_name_path = os.path.join('/dev/', device_item.name) ongoing_item_config.original_dev_path = os.path.join('/dev/', device_item.name) else: ongoing_item_config.original_dev_name_path = os.path.join('/dev/mapper/', device_item.name) ongoing_item_config.original_dev_path = os.path.join('/dev/mapper/', device_item.name) ongoing_item_config.phase = CommonVariables.EncryptionPhaseBackupHeader ongoing_item_config.commit() else: logger.log(msg="ongoing item config is not none, this is resuming, info: {0}".format(ongoing_item_config), level=CommonVariables.WarningLevel) logger.log(msg=("encrypting device item: {0}".format(ongoing_item_config.get_original_dev_path()))) # we only support ext file systems. current_phase = ongoing_item_config.get_phase() original_dev_path = ongoing_item_config.get_original_dev_path() mapper_name = ongoing_item_config.get_mapper_name() device_size = ongoing_item_config.get_device_size() luks_header_size = CommonVariables.luks_header_size size_shrink_to = (device_size - luks_header_size) / CommonVariables.sector_size while current_phase != CommonVariables.EncryptionPhaseDone: if current_phase == CommonVariables.EncryptionPhaseBackupHeader: logger.log(msg="the current phase is " + str(CommonVariables.EncryptionPhaseBackupHeader), level=CommonVariables.InfoLevel) # log an appropriate warning if the file system type is not supported device_fs = ongoing_item_config.get_file_system().lower() if not device_fs in CommonVariables.inplace_supported_file_systems: if device_fs in CommonVariables.format_supported_file_systems: msg = "Encrypting {0} file system is not supported for data-preserving encryption. Consider using the encrypt-format-all option.".format(device_fs) else: msg = "AzureDiskEncryption does not support the {0} file system".format(device_fs) logger.log(msg=msg, level=CommonVariables.WarningLevel) ongoing_item_config.clear_config() return current_phase chk_shrink_result = disk_util.check_shrink_fs(dev_path=original_dev_path, size_shrink_to=size_shrink_to) if chk_shrink_result != CommonVariables.process_success: logger.log(msg="check shrink fs failed with code {0} for {1}".format(chk_shrink_result, original_dev_path), level=CommonVariables.ErrorLevel) logger.log(msg="your file system may not have enough space to do the encryption.") # remove the ongoing item. ongoing_item_config.clear_config() return current_phase else: ongoing_item_config.current_slice_index = 0 ongoing_item_config.current_source_path = original_dev_path ongoing_item_config.current_destination = encryption_environment.copy_header_slice_file_path ongoing_item_config.current_total_copy_size = CommonVariables.default_block_size ongoing_item_config.from_end = False ongoing_item_config.header_slice_file_path = encryption_environment.copy_header_slice_file_path ongoing_item_config.original_dev_path = original_dev_path ongoing_item_config.commit() if os.path.exists(encryption_environment.copy_header_slice_file_path): logger.log(msg="the header slice file is there, remove it.", level=CommonVariables.WarningLevel) os.remove(encryption_environment.copy_header_slice_file_path) copy_result = disk_util.copy(ongoing_item_config=ongoing_item_config, status_prefix=status_prefix) if copy_result != CommonVariables.process_success: logger.log(msg="copy the header block failed, return code is: {0}".format(copy_result), level=CommonVariables.ErrorLevel) return current_phase else: ongoing_item_config.current_slice_index = 0 ongoing_item_config.phase = CommonVariables.EncryptionPhaseEncryptDevice ongoing_item_config.commit() current_phase = CommonVariables.EncryptionPhaseEncryptDevice elif current_phase == CommonVariables.EncryptionPhaseEncryptDevice: logger.log(msg="the current phase is {0}".format(CommonVariables.EncryptionPhaseEncryptDevice), level=CommonVariables.InfoLevel) encrypt_result = disk_util.encrypt_disk(dev_path=original_dev_path, passphrase_file=passphrase_file, mapper_name=mapper_name, header_file=None) # after the encrypt_disk without seperate header, then the uuid # would change. if encrypt_result != CommonVariables.process_success: logger.log(msg="encrypt file system failed.", level=CommonVariables.ErrorLevel) return current_phase else: ongoing_item_config.current_slice_index = 0 ongoing_item_config.phase = CommonVariables.EncryptionPhaseCopyData ongoing_item_config.commit() current_phase = CommonVariables.EncryptionPhaseCopyData elif current_phase == CommonVariables.EncryptionPhaseCopyData: logger.log(msg="the current phase is {0}".format(CommonVariables.EncryptionPhaseCopyData), level=CommonVariables.InfoLevel) device_mapper_path = os.path.join(CommonVariables.dev_mapper_root, mapper_name) ongoing_item_config.current_destination = device_mapper_path ongoing_item_config.current_source_path = original_dev_path ongoing_item_config.current_total_copy_size = (device_size - luks_header_size) ongoing_item_config.from_end = True ongoing_item_config.phase = CommonVariables.EncryptionPhaseCopyData ongoing_item_config.commit() copy_result = disk_util.copy(ongoing_item_config=ongoing_item_config, status_prefix=status_prefix) if copy_result != CommonVariables.process_success: logger.log(msg="copy the main content block failed, return code is: {0}".format(copy_result), level=CommonVariables.ErrorLevel) return current_phase else: ongoing_item_config.phase = CommonVariables.EncryptionPhaseRecoverHeader ongoing_item_config.commit() current_phase = CommonVariables.EncryptionPhaseRecoverHeader elif current_phase == CommonVariables.EncryptionPhaseRecoverHeader: logger.log(msg="the current phase is " + str(CommonVariables.EncryptionPhaseRecoverHeader), level=CommonVariables.InfoLevel) ongoing_item_config.from_end = False backed_up_header_slice_file_path = ongoing_item_config.get_header_slice_file_path() ongoing_item_config.current_slice_index = 0 ongoing_item_config.current_source_path = backed_up_header_slice_file_path device_mapper_path = os.path.join(CommonVariables.dev_mapper_root, mapper_name) ongoing_item_config.current_destination = device_mapper_path ongoing_item_config.current_total_copy_size = CommonVariables.default_block_size ongoing_item_config.commit() copy_result = disk_util.copy(ongoing_item_config=ongoing_item_config, status_prefix=status_prefix) if copy_result == CommonVariables.process_success: crypt_item_to_update = CryptItem() crypt_item_to_update.mapper_name = mapper_name original_dev_name_path = ongoing_item_config.get_original_dev_name_path() crypt_item_to_update.dev_path = disk_util.get_persistent_path_by_sdx_path(original_dev_name_path) crypt_item_to_update.luks_header_path = "None" crypt_item_to_update.file_system = ongoing_item_config.get_file_system() crypt_item_to_update.uses_cleartext_key = False crypt_item_to_update.current_luks_slot = 0 # if the original mountpoint is empty, then leave # it as None mount_point = ongoing_item_config.get_mount_point() if mount_point == "" or mount_point is None: crypt_item_to_update.mount_point = "None" else: crypt_item_to_update.mount_point = mount_point update_crypt_item_result = disk_util.add_crypt_item(crypt_item_to_update, passphrase_file) if not update_crypt_item_result: logger.log(msg="update crypt item failed", level=CommonVariables.ErrorLevel) if mount_point: logger.log(msg="removing entry for unencrypted drive from fstab", level=CommonVariables.InfoLevel) disk_util.modify_fstab_entry_encrypt(mount_point, os.path.join(CommonVariables.dev_mapper_root, mapper_name)) else: logger.log(msg=original_dev_name_path + " is not defined in fstab, no need to update", level=CommonVariables.InfoLevel) if os.path.exists(encryption_environment.copy_header_slice_file_path): os.remove(encryption_environment.copy_header_slice_file_path) current_phase = CommonVariables.EncryptionPhaseDone ongoing_item_config.phase = current_phase ongoing_item_config.commit() expand_fs_result = disk_util.expand_fs(dev_path=device_mapper_path) if crypt_item_to_update.mount_point != "None": disk_util.mount_filesystem(device_mapper_path, ongoing_item_config.get_mount_point()) else: logger.log("the crypt_item_to_update.mount_point is None, so we do not mount it.") ongoing_item_config.clear_config() if expand_fs_result != CommonVariables.process_success: logger.log(msg="expand fs result is: {0}".format(expand_fs_result), level=CommonVariables.ErrorLevel) return current_phase else: logger.log(msg="recover header failed result is: {0}".format(copy_result), level=CommonVariables.ErrorLevel) return current_phase def encrypt_inplace_with_seperate_header_file(passphrase_file, device_item, disk_util, bek_util, status_prefix='', ongoing_item_config=None): """ if ongoing_item_config is not None, then this is a resume case. """ logger.log("encrypt_inplace_with_seperate_header_file") current_phase = CommonVariables.EncryptionPhaseEncryptDevice if ongoing_item_config is None: ongoing_item_config = OnGoingItemConfig(encryption_environment=encryption_environment, logger=logger) mapper_name = str(uuid.uuid4()) ongoing_item_config.current_block_size = CommonVariables.default_block_size ongoing_item_config.current_slice_index = 0 ongoing_item_config.device_size = device_item.size ongoing_item_config.file_system = device_item.file_system ongoing_item_config.mapper_name = mapper_name ongoing_item_config.mount_point = device_item.mount_point # TODO improve this. if os.path.exists(os.path.join('/dev/', device_item.name)): ongoing_item_config.original_dev_name_path = os.path.join('/dev/', device_item.name) else: ongoing_item_config.original_dev_name_path = os.path.join('/dev/mapper/', device_item.name) ongoing_item_config.original_dev_path = os.path.join('/dev/disk/by-uuid', device_item.uuid) luks_header_file_path = disk_util.create_luks_header(mapper_name=mapper_name) if luks_header_file_path is None: logger.log(msg="create header file failed", level=CommonVariables.ErrorLevel) return current_phase else: ongoing_item_config.luks_header_file_path = luks_header_file_path ongoing_item_config.phase = CommonVariables.EncryptionPhaseEncryptDevice ongoing_item_config.commit() else: logger.log(msg="ongoing item config is not none, this is resuming: {0}".format(ongoing_item_config), level=CommonVariables.WarningLevel) current_phase = ongoing_item_config.get_phase() while current_phase != CommonVariables.EncryptionPhaseDone: if current_phase == CommonVariables.EncryptionPhaseEncryptDevice: try: mapper_name = ongoing_item_config.get_mapper_name() original_dev_path = ongoing_item_config.get_original_dev_path() luks_header_file_path = ongoing_item_config.get_header_file_path() toggle_se_linux_for_centos7(True) encrypt_result = disk_util.encrypt_disk(dev_path=original_dev_path, passphrase_file=passphrase_file, mapper_name=mapper_name, header_file=luks_header_file_path) if encrypt_result != CommonVariables.process_success: logger.log(msg="the encrypton for {0} failed".format(original_dev_path), level=CommonVariables.ErrorLevel) return current_phase else: ongoing_item_config.phase = CommonVariables.EncryptionPhaseCopyData ongoing_item_config.commit() current_phase = CommonVariables.EncryptionPhaseCopyData finally: toggle_se_linux_for_centos7(False) elif current_phase == CommonVariables.EncryptionPhaseCopyData: try: mapper_name = ongoing_item_config.get_mapper_name() original_dev_path = ongoing_item_config.get_original_dev_path() luks_header_file_path = ongoing_item_config.get_header_file_path() toggle_se_linux_for_centos7(True) device_mapper_path = os.path.join("/dev/mapper", mapper_name) if not os.path.exists(device_mapper_path): open_result = disk_util.luks_open(passphrase_file=passphrase_file, dev_path=original_dev_path, mapper_name=mapper_name, header_file=luks_header_file_path, uses_cleartext_key=False) if open_result != CommonVariables.process_success: logger.log(msg="the luks open for {0} failed.".format(original_dev_path), level=CommonVariables.ErrorLevel) return current_phase else: logger.log(msg="the device mapper path existed, so skip the luks open.", level=CommonVariables.InfoLevel) device_size = ongoing_item_config.get_device_size() current_slice_index = ongoing_item_config.get_current_slice_index() if current_slice_index is None: ongoing_item_config.current_slice_index = 0 ongoing_item_config.current_source_path = original_dev_path ongoing_item_config.current_destination = device_mapper_path ongoing_item_config.current_total_copy_size = device_size ongoing_item_config.from_end = True ongoing_item_config.commit() copy_result = disk_util.copy(ongoing_item_config=ongoing_item_config, status_prefix=status_prefix) if copy_result != CommonVariables.success: error_message = "the copying result is {0} so skip the mounting".format(copy_result) logger.log(msg=(error_message), level=CommonVariables.ErrorLevel) return current_phase else: crypt_item_to_update = CryptItem() crypt_item_to_update.mapper_name = mapper_name original_dev_name_path = ongoing_item_config.get_original_dev_name_path() crypt_item_to_update.dev_path = disk_util.get_persistent_path_by_sdx_path(original_dev_name_path) crypt_item_to_update.luks_header_path = luks_header_file_path crypt_item_to_update.file_system = ongoing_item_config.get_file_system() crypt_item_to_update.uses_cleartext_key = False crypt_item_to_update.current_luks_slot = 0 # if the original mountpoint is empty, then leave # it as None mount_point = ongoing_item_config.get_mount_point() if mount_point is None or mount_point == "": crypt_item_to_update.mount_point = "None" else: crypt_item_to_update.mount_point = mount_point update_crypt_item_result = disk_util.add_crypt_item(crypt_item_to_update, passphrase_file) if not update_crypt_item_result: logger.log(msg="update crypt item failed", level=CommonVariables.ErrorLevel) if crypt_item_to_update.mount_point != "None": disk_util.mount_filesystem(device_mapper_path, mount_point) else: logger.log("the crypt_item_to_update.mount_point is None, so we do not mount it.") if mount_point: logger.log(msg="removing entry for unencrypted drive from fstab", level=CommonVariables.InfoLevel) disk_util.modify_fstab_entry_encrypt(mount_point, os.path.join(CommonVariables.dev_mapper_root, mapper_name)) else: logger.log(msg=original_dev_name_path + " is not defined in fstab, no need to update", level=CommonVariables.InfoLevel) current_phase = CommonVariables.EncryptionPhaseDone ongoing_item_config.phase = current_phase ongoing_item_config.commit() ongoing_item_config.clear_config() return current_phase finally: toggle_se_linux_for_centos7(False) def decrypt_inplace_copy_data(passphrase_file, crypt_item, raw_device_item, mapper_device_item, disk_util, status_prefix='', ongoing_item_config=None): logger.log(msg="decrypt_inplace_copy_data") if ongoing_item_config: logger.log(msg="ongoing item config is not none, resuming decryption, info: {0}".format(ongoing_item_config), level=CommonVariables.WarningLevel) else: logger.log(msg="starting decryption of {0}".format(crypt_item)) ongoing_item_config = OnGoingItemConfig(encryption_environment=encryption_environment, logger=logger) ongoing_item_config.current_destination = crypt_item.dev_path ongoing_item_config.current_source_path = os.path.join(CommonVariables.dev_mapper_root, crypt_item.mapper_name) ongoing_item_config.current_total_copy_size = mapper_device_item.size ongoing_item_config.from_end = True ongoing_item_config.phase = CommonVariables.DecryptionPhaseCopyData ongoing_item_config.current_slice_index = 0 ongoing_item_config.current_block_size = CommonVariables.default_block_size ongoing_item_config.mount_point = crypt_item.mount_point ongoing_item_config.commit() current_phase = ongoing_item_config.get_phase() while current_phase != CommonVariables.DecryptionPhaseDone: logger.log(msg=("the current phase is {0}".format(CommonVariables.EncryptionPhaseBackupHeader)), level=CommonVariables.InfoLevel) if current_phase == CommonVariables.DecryptionPhaseCopyData: copy_result = disk_util.copy(ongoing_item_config=ongoing_item_config, status_prefix=status_prefix) if copy_result == CommonVariables.process_success: mount_point = ongoing_item_config.get_mount_point() if mount_point and mount_point != "None": logger.log(msg="restoring entry for unencrypted drive from fstab", level=CommonVariables.InfoLevel) disk_util.restore_mount_info(ongoing_item_config.get_mount_point()) elif crypt_item.mapper_name: disk_util.restore_mount_info(crypt_item.mapper_name) else: logger.log(msg=crypt_item.dev_path + " was not in fstab when encryption was enabled, no need to restore", level=CommonVariables.InfoLevel) ongoing_item_config.phase = CommonVariables.DecryptionPhaseDone ongoing_item_config.commit() current_phase = CommonVariables.DecryptionPhaseDone else: logger.log(msg="decryption: block copy failed, result: {0}".format(copy_result), level=CommonVariables.ErrorLevel) return current_phase ongoing_item_config.clear_config() return current_phase def decrypt_inplace_without_separate_header_file(passphrase_file, crypt_item, raw_device_item, mapper_device_item, disk_util, status_prefix='', ongoing_item_config=None): logger.log(msg="decrypt_inplace_without_separate_header_file") proc_comm = ProcessCommunicator() executor = CommandExecutor(logger) executor.Execute(DistroPatcher.cryptsetup_path + " luksDump " + crypt_item.dev_path, communicator=proc_comm) luks_header_size = int(re.findall(r"Payload.*?(\d+)", proc_comm.stdout)[0]) * CommonVariables.sector_size if raw_device_item.size - mapper_device_item.size != luks_header_size: logger.log(msg="mismatch between raw and mapper device found for crypt_item {0}".format(crypt_item), level=CommonVariables.ErrorLevel) logger.log(msg="raw_device_item: {0}".format(raw_device_item), level=CommonVariables.ErrorLevel) logger.log(msg="mapper_device_item {0}".format(mapper_device_item), level=CommonVariables.ErrorLevel) return None return decrypt_inplace_copy_data(passphrase_file, crypt_item, raw_device_item, mapper_device_item, disk_util, status_prefix, ongoing_item_config) def decrypt_inplace_with_separate_header_file(passphrase_file, crypt_item, raw_device_item, mapper_device_item, disk_util, status_prefix='', ongoing_item_config=None): logger.log(msg="decrypt_inplace_with_separate_header_file") if raw_device_item.size != mapper_device_item.size: logger.log(msg="mismatch between raw and mapper device found for crypt_item {0}".format(crypt_item), level=CommonVariables.ErrorLevel) logger.log(msg="raw_device_item: {0}".format(raw_device_item), level=CommonVariables.ErrorLevel) logger.log(msg="mapper_device_item {0}".format(mapper_device_item), level=CommonVariables.ErrorLevel) return return decrypt_inplace_copy_data(passphrase_file, crypt_item, raw_device_item, mapper_device_item, disk_util, status_prefix, ongoing_item_config) def enable_encryption_all_format(passphrase_file, encryption_marker, disk_util, bek_util): """ In case of success return None, otherwise return the device item which failed. """ logger.log(msg="executing the enable_encryption_all_format command") device_items = find_all_devices_to_encrypt(encryption_marker, disk_util, bek_util) # Don't encrypt partitions that are not even mounted device_items_to_encrypt = filter(lambda di: di.mount_point is not None and di.mount_point != "", device_items) dev_path_reference_table = disk_util.get_block_device_to_azure_udev_table() device_items_to_encrypt = filter(lambda di: os.path.join('/dev/', di.name) in dev_path_reference_table, device_items_to_encrypt) msg = 'Encrypting and formatting {0} data volumes'.format(len(device_items_to_encrypt)) logger.log(msg) hutil.do_status_report(operation='EnableEncryptionFormatAll', status=CommonVariables.extension_success_status, status_code=str(CommonVariables.success), message=msg) return encrypt_format_device_items(passphrase_file, device_items_to_encrypt, disk_util, True) def encrypt_format_device_items(passphrase, device_items, disk_util, force=False): """ Formats the block devices represented by the supplied device_item. This is done by constructing a disk format query based on the supplied device items and passing it on to the enable_encryption_format method. Returns None if all items are successfully format-encrypted Otherwise returns the device item which failed. """ # use the new udev names for formatting and later on for cryptmounting dev_path_reference_table = disk_util.get_block_device_to_azure_udev_table() def single_device_item_to_format_query_dict(device_item): """ Converts a single device_item into an dictionary than will be later "json-stringified" """ format_query_element = {} dev_path = os.path.join('/dev/', device_item.name) if dev_path in dev_path_reference_table: format_query_element["dev_path"] = dev_path_reference_table[dev_path] else: format_query_element["dev_path"] = dev_path # introduce a new "full_mount_point" field below to avoid the /mnt/ prefix that automatically gets appended format_query_element["full_mount_point"] = str(device_item.mount_point) format_query_element["file_system"] = str(device_item.file_system) return format_query_element disk_format_query = json.dumps(map(single_device_item_to_format_query_dict, device_items)) return enable_encryption_format(passphrase, disk_format_query, disk_util, force) def find_all_devices_to_encrypt(encryption_marker, disk_util, bek_util): device_items = disk_util.get_device_items(None) device_items_to_encrypt = [] special_azure_devices_to_skip = disk_util.get_azure_devices() for device_item in device_items: logger.log("device_item == " + str(device_item)) should_skip = disk_util.should_skip_for_inplace_encryption(device_item, special_azure_devices_to_skip, encryption_marker.get_volume_type()) if not should_skip and \ not any(di.name == device_item.name for di in device_items_to_encrypt): device_items_to_encrypt.append(device_item) return device_items_to_encrypt def enable_encryption_all_in_place(passphrase_file, encryption_marker, disk_util, bek_util): """ if return None for the success case, or return the device item which failed. """ logger.log(msg="executing the enable_encryption_all_in_place command.") device_items_to_encrypt = find_all_devices_to_encrypt(encryption_marker, disk_util, bek_util) msg = 'Encrypting {0} data volumes'.format(len(device_items_to_encrypt)) logger.log(msg) hutil.do_status_report(operation='EnableEncryption', status=CommonVariables.extension_success_status, status_code=str(CommonVariables.success), message=msg) for device_num, device_item in enumerate(device_items_to_encrypt): umount_status_code = CommonVariables.success if device_item.mount_point is not None and device_item.mount_point != "": umount_status_code = disk_util.umount(device_item.mount_point) if umount_status_code != CommonVariables.success: logger.log("error occured when do the umount for: {0} with code: {1}".format(device_item.mount_point, umount_status_code)) else: logger.log(msg=("encrypting: {0}".format(device_item))) no_header_file_support = not_support_header_option_distro(DistroPatcher) status_prefix = "Encrypting data volume {0}/{1}".format(device_num + 1, len(device_items_to_encrypt)) # TODO check the file system before encrypting it. if no_header_file_support: logger.log(msg="this is the centos 6 or redhat 6 or sles 11 series, need to resize data drive", level=CommonVariables.WarningLevel) encryption_result_phase = encrypt_inplace_without_seperate_header_file(passphrase_file=passphrase_file, device_item=device_item, disk_util=disk_util, bek_util=bek_util, status_prefix=status_prefix) else: encryption_result_phase = encrypt_inplace_with_seperate_header_file(passphrase_file=passphrase_file, device_item=device_item, disk_util=disk_util, bek_util=bek_util, status_prefix=status_prefix) if encryption_result_phase == CommonVariables.EncryptionPhaseDone: continue else: # do exit to exit from this round return device_item return None def disable_encryption_all_in_place(passphrase_file, decryption_marker, disk_util): """ On success, returns None. Otherwise returns the crypt item for which decryption failed. """ logger.log(msg="executing disable_encryption_all_in_place") device_items = disk_util.get_device_items(None) crypt_items = disk_util.get_crypt_items() msg = 'Decrypting {0} data volumes'.format(len(crypt_items)) logger.log(msg) hutil.do_status_report(operation='DisableEncryption', status=CommonVariables.extension_success_status, status_code=str(CommonVariables.success), message=msg) for crypt_item_num, crypt_item in enumerate(crypt_items): logger.log("processing crypt_item: " + str(crypt_item)) def raw_device_item_match(device_item): sdx_device_name = os.path.join("/dev/", device_item.name) if crypt_item.dev_path.startswith(CommonVariables.disk_by_id_root): return crypt_item.dev_path == disk_util.query_dev_id_path_by_sdx_path(sdx_device_name) else: return crypt_item.dev_path == sdx_device_name def mapped_device_item_match(device_item): return crypt_item.mapper_name == device_item.name raw_device_item = next((d for d in device_items if raw_device_item_match(d)), None) mapper_device_item = next((d for d in device_items if mapped_device_item_match(d)), None) if not raw_device_item: logger.log("raw device not found for crypt_item {0}".format(crypt_item), level='Warn') logger.log("Skipping device", level='Warn') continue if not mapper_device_item: logger.log("mapper device not found for crypt_item {0}".format(crypt_item)) if disk_util.is_luks_device(crypt_item.dev_path, crypt_item.luks_header_path): logger.log("Found a luks device for this device item, yet couldn't open mapper: {0}".format(crypt_item)) logger.log("Failing".format(crypt_item)) return crypt_item else: continue decryption_result_phase = None status_prefix = "Decrypting data volume {0}/{1}".format(crypt_item_num + 1, len(crypt_items)) if crypt_item.luks_header_path: decryption_result_phase = decrypt_inplace_with_separate_header_file(passphrase_file=passphrase_file, crypt_item=crypt_item, raw_device_item=raw_device_item, mapper_device_item=mapper_device_item, disk_util=disk_util, status_prefix=status_prefix) else: decryption_result_phase = decrypt_inplace_without_separate_header_file(passphrase_file=passphrase_file, crypt_item=crypt_item, raw_device_item=raw_device_item, mapper_device_item=mapper_device_item, disk_util=disk_util, status_prefix=status_prefix) if decryption_result_phase == CommonVariables.DecryptionPhaseDone: disk_util.luks_close(crypt_item.mapper_name) disk_util.remove_crypt_item(crypt_item) #disk_util.mount_all() continue else: # decryption failed for a crypt_item, return the failed item to caller return crypt_item disk_util.mount_all() return None def daemon_encrypt(): # Ensure the same configuration is executed only once # If the previous enable failed, we do not have retry logic here. # TODO Remount all encryption_marker = EncryptionMarkConfig(logger, encryption_environment) if encryption_marker.config_file_exists(): logger.log("encryption is marked.") """ search for the bek volume, then mount it:) """ disk_util = DiskUtil(hutil, DistroPatcher, logger, encryption_environment) encryption_config = EncryptionConfig(encryption_environment, logger) bek_passphrase_file = None """ try to find the attached bek volume, and use the file to mount the crypted volumes, and if the passphrase file is found, then we will re-use it for the future. """ bek_util = BekUtil(disk_util, logger) if encryption_config.config_file_exists(): bek_passphrase_file = bek_util.get_bek_passphrase_file(encryption_config) if bek_passphrase_file is None: hutil.do_exit(exit_code=CommonVariables.passphrase_file_not_found, operation='EnableEncryption', status=CommonVariables.extension_error_status, code=CommonVariables.passphrase_file_not_found, message='Passphrase file not found.') executor = CommandExecutor(logger) is_not_in_stripped_os = bool(executor.Execute("mountpoint /oldroot")) volume_type = encryption_config.get_volume_type().lower() if (volume_type == CommonVariables.VolumeTypeData.lower() or volume_type == CommonVariables.VolumeTypeAll.lower()) and \ is_not_in_stripped_os: try: while not daemon_encrypt_data_volumes(encryption_marker=encryption_marker, encryption_config=encryption_config, disk_util=disk_util, bek_util=bek_util, bek_passphrase_file=bek_passphrase_file): logger.log("Calling daemon_encrypt_data_volumes again") except Exception as e: message = "Failed to encrypt data volumes with error: {0}, stack trace: {1}".format(e, traceback.format_exc()) logger.log(msg=message, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation='EnableEncryptionDataVolumes', status=CommonVariables.extension_error_status, code=CommonVariables.encryption_failed, message=message) else: hutil.do_status_report(operation='EnableEncryptionDataVolumes', status=CommonVariables.extension_success_status, status_code=str(CommonVariables.success), message='Encryption succeeded for data volumes') disk_util.log_lsblk_output() mount_encrypted_disks(disk_util, bek_util, bek_passphrase_file, encryption_config) if volume_type == CommonVariables.VolumeTypeOS.lower() or \ volume_type == CommonVariables.VolumeTypeAll.lower(): # import OSEncryption here instead of at the top because it relies # on pre-req packages being installed (specifically, python-six on Ubuntu) distro_name = DistroPatcher.distro_info[0] distro_version = DistroPatcher.distro_info[1] os_encryption = None if (((distro_name == 'redhat' and distro_version == '7.3') or (distro_name == 'redhat' and distro_version == '7.4') or (distro_name == 'redhat' and distro_version == '7.5') or (distro_name == 'redhat' and distro_version == '7.6') or (distro_name == 'redhat' and distro_version == '7.7')) and (disk_util.is_os_disk_lvm() or os.path.exists('/volumes.lvm'))): from oscrypto.rhel_72_lvm import RHEL72LVMEncryptionStateMachine os_encryption = RHEL72LVMEncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) elif (((distro_name == 'centos' and distro_version == '7.3.1611') or (distro_name == 'centos' and distro_version.startswith('7.4')) or (distro_name == 'centos' and distro_version.startswith('7.5')) or (distro_name == 'centos' and distro_version.startswith('7.6')) or (distro_name == 'centos' and distro_version.startswith('7.7'))) and (disk_util.is_os_disk_lvm() or os.path.exists('/volumes.lvm'))): from oscrypto.rhel_72_lvm import RHEL72LVMEncryptionStateMachine os_encryption = RHEL72LVMEncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) elif ((distro_name == 'redhat' and distro_version == '7.2') or (distro_name == 'redhat' and distro_version == '7.3') or (distro_name == 'redhat' and distro_version == '7.4') or (distro_name == 'redhat' and distro_version == '7.5') or (distro_name == 'redhat' and distro_version == '7.6') or (distro_name == 'redhat' and distro_version == '7.7') or (distro_name == 'centos' and distro_version.startswith('7.7')) or (distro_name == 'centos' and distro_version.startswith('7.6')) or (distro_name == 'centos' and distro_version.startswith('7.5')) or (distro_name == 'centos' and distro_version.startswith('7.4')) or (distro_name == 'centos' and distro_version == '7.3.1611') or (distro_name == 'centos' and distro_version == '7.2.1511')): from oscrypto.rhel_72 import RHEL72EncryptionStateMachine os_encryption = RHEL72EncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) elif distro_name == 'redhat' and distro_version == '6.8': from oscrypto.rhel_68 import RHEL68EncryptionStateMachine os_encryption = RHEL68EncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) elif distro_name == 'centos' and (distro_version == '6.8' or distro_version == '6.9'): from oscrypto.centos_68 import CentOS68EncryptionStateMachine os_encryption = CentOS68EncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) elif distro_name == 'Ubuntu' and distro_version in ['16.04', '18.04']: from oscrypto.ubuntu_1604 import Ubuntu1604EncryptionStateMachine os_encryption = Ubuntu1604EncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) elif distro_name == 'Ubuntu' and distro_version == '14.04': from oscrypto.ubuntu_1404 import Ubuntu1404EncryptionStateMachine os_encryption = Ubuntu1404EncryptionStateMachine(hutil=hutil, distro_patcher=DistroPatcher, logger=logger, encryption_environment=encryption_environment) else: message = "OS volume encryption is not supported on {0} {1}".format(distro_name, distro_version) logger.log(msg=message, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation='EnableEncryptionOSVolume', status=CommonVariables.extension_error_status, code=CommonVariables.encryption_failed, message=message) try: os_encryption.start_encryption() if not os_encryption.state == 'completed': raise Exception("did not reach completed state") else: encryption_marker.clear_config() except Exception as e: message = "Failed to encrypt OS volume with error: {0}, stack trace: {1}, machine state: {2}".format(e, traceback.format_exc(), os_encryption.state) logger.log(msg=message, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation='EnableEncryptionOSVolume', status=CommonVariables.extension_error_status, code=CommonVariables.encryption_failed, message=message) message = '' if volume_type == CommonVariables.VolumeTypeAll.lower(): message = 'Encryption succeeded for all volumes' else: message = 'Encryption succeeded for OS volume' logger.log(msg=message) hutil.do_status_report(operation='EnableEncryptionOSVolume', status=CommonVariables.extension_success_status, status_code=str(CommonVariables.success), message=message) def daemon_encrypt_data_volumes(encryption_marker, encryption_config, disk_util, bek_util, bek_passphrase_file): try: """ check whether there's a scheduled encryption task """ mount_all_result = disk_util.mount_all() if mount_all_result != CommonVariables.process_success: logger.log(msg="mount all failed with code:{0}".format(mount_all_result), level=CommonVariables.ErrorLevel) """ TODO: resuming the encryption for rebooting suddenly scenario we need the special handling is because the half done device can be a error state: say, the file system header missing.so it could be identified. """ ongoing_item_config = OnGoingItemConfig(encryption_environment=encryption_environment, logger=logger) if ongoing_item_config.config_file_exists(): logger.log("OngoingItemConfig exists.") ongoing_item_config.load_value_from_file() header_file_path = ongoing_item_config.get_header_file_path() mount_point = ongoing_item_config.get_mount_point() status_prefix = "Resuming encryption after reboot" if not none_or_empty(mount_point): logger.log("mount point is not empty {0}, trying to unmount it first.".format(mount_point)) umount_status_code = disk_util.umount(mount_point) logger.log("unmount return code is {0}".format(umount_status_code)) if none_or_empty(header_file_path): encryption_result_phase = encrypt_inplace_without_seperate_header_file(passphrase_file=bek_passphrase_file, device_item=None, disk_util=disk_util, bek_util=bek_util, status_prefix=status_prefix, ongoing_item_config=ongoing_item_config) # TODO mount it back when shrink failed else: encryption_result_phase = encrypt_inplace_with_seperate_header_file(passphrase_file=bek_passphrase_file, device_item=None, disk_util=disk_util, bek_util=bek_util, status_prefix=status_prefix, ongoing_item_config=ongoing_item_config) """ if the resuming failed, we should fail. """ if encryption_result_phase != CommonVariables.EncryptionPhaseDone: original_dev_path = ongoing_item_config.get_original_dev_path message = 'EnableEncryption: resuming encryption for {0} failed'.format(original_dev_path) raise Exception(message) else: ongoing_item_config.clear_config() else: logger.log("OngoingItemConfig does not exist") failed_item = None if not encryption_marker.config_file_exists(): logger.log("Data volumes are not marked for encryption") return True if encryption_marker.get_current_command() == CommonVariables.EnableEncryption: failed_item = enable_encryption_all_in_place(passphrase_file=bek_passphrase_file, encryption_marker=encryption_marker, disk_util=disk_util, bek_util=bek_util) elif encryption_marker.get_current_command() == CommonVariables.EnableEncryptionFormat: disk_format_query = encryption_marker.get_encryption_disk_format_query() failed_item = enable_encryption_format(passphrase=bek_passphrase_file, disk_format_query=disk_format_query, disk_util=disk_util) elif encryption_marker.get_current_command() == CommonVariables.EnableEncryptionFormatAll: failed_item = enable_encryption_all_format(passphrase_file=bek_passphrase_file, encryption_marker=encryption_marker, disk_util=disk_util, bek_util=bek_util) else: message = "Command {0} not supported.".format(encryption_marker.get_current_command()) logger.log(msg=message, level=CommonVariables.ErrorLevel) raise Exception(message) if failed_item: message = 'Encryption failed for {0}'.format(failed_item) raise Exception(message) else: return True except Exception: raise def daemon_decrypt(): decryption_marker = DecryptionMarkConfig(logger, encryption_environment) if not decryption_marker.config_file_exists(): logger.log("decryption is not marked.") return logger.log("decryption is marked.") # mount and then unmount all the encrypted items # in order to set-up all the mapper devices # we don't need the BEK since all the drives that need decryption were made cleartext-key unlockable by first call to disable disk_util = DiskUtil(hutil, DistroPatcher, logger, encryption_environment) encryption_config = EncryptionConfig(encryption_environment, logger) mount_encrypted_disks(disk_util=disk_util, bek_util=None, encryption_config=encryption_config, passphrase_file=None) disk_util.umount_all_crypt_items() # at this point all the /dev/mapper/* crypt devices should be open ongoing_item_config = OnGoingItemConfig(encryption_environment=encryption_environment, logger=logger) if ongoing_item_config.config_file_exists(): logger.log("ongoing item config exists.") else: logger.log("ongoing item config does not exist.") failed_item = None if decryption_marker.get_current_command() == CommonVariables.DisableEncryption: failed_item = disable_encryption_all_in_place(passphrase_file=None, decryption_marker=decryption_marker, disk_util=disk_util) else: raise Exception("command {0} not supported.".format(decryption_marker.get_current_command())) if failed_item is not None: hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation='Disable', status=CommonVariables.extension_error_status, code=CommonVariables.encryption_failed, message='Decryption failed for {0}'.format(failed_item)) else: encryption_config.clear_config() logger.log("clearing the decryption mark after successful decryption") decryption_marker.clear_config() hutil.do_exit(exit_code=0, operation='Disable', status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message='Decryption succeeded') def daemon(): hutil.find_last_nonquery_operation = True hutil.do_parse_context('Executing') lock = ProcessLock(logger, encryption_environment.daemon_lock_file_path) if not lock.try_lock(): logger.log("there's another daemon running, please wait it to exit.", level=CommonVariables.WarningLevel) return logger.log("daemon lock acquired sucessfully.") logger.log("waiting for 2 minutes before continuing the daemon") time.sleep(120) logger.log("Installing pre-requisites") DistroPatcher.install_extras() # try decrypt, if decryption marker exists decryption_marker = DecryptionMarkConfig(logger, encryption_environment) if decryption_marker.config_file_exists(): try: daemon_decrypt() except Exception as e: error_msg = ("Failed to disable the extension with error: {0}, stack trace: {1}".format(e, traceback.format_exc())) logger.log(msg=error_msg, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation='Disable', status=CommonVariables.extension_error_status, code=str(CommonVariables.encryption_failed), message=error_msg) finally: lock.release_lock() logger.log("returned to daemon") logger.log("exiting daemon") return # try encrypt, in absence of decryption marker try: daemon_encrypt() except Exception as e: # mount the file systems back. error_msg = ("Failed to enable the extension with error: {0}, stack trace: {1}".format(e, traceback.format_exc())) logger.log(msg=error_msg, level=CommonVariables.ErrorLevel) hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation='Enable', status=CommonVariables.extension_error_status, code=str(CommonVariables.encryption_failed), message=error_msg) else: encryption_marker = EncryptionMarkConfig(logger, encryption_environment) # TODO not remove it, backed it up. logger.log("returned to daemon successfully after encryption") logger.log("clearing the encryption mark.") encryption_marker.clear_config() hutil.redo_current_status() finally: lock.release_lock() logger.log("exiting daemon") def start_daemon(operation): # This process will start a new background process by calling # extension_shim.sh -c handle.py -daemon # to run the script and will exit itself immediatelly. shim_path = os.path.join(os.getcwd(), CommonVariables.extension_shim_filename) shim_opts = '-c ' + os.path.join(os.getcwd(), __file__) + ' -daemon' args = [shim_path, shim_opts] logger.log("start_daemon with args: {0}".format(args)) # Redirect stdout and stderr to /dev/null. Otherwise daemon process will # throw broken pipe exception when parent process exit. devnull = open(os.devnull, 'w') subprocess.Popen(args, stdout=devnull, stderr=devnull) encryption_config = EncryptionConfig(encryption_environment, logger) if encryption_config.config_file_exists(): hutil.do_exit(exit_code=0, operation=operation, status=CommonVariables.extension_success_status, code=str(CommonVariables.success), message=encryption_config.get_secret_id()) else: hutil.do_exit(exit_code=CommonVariables.encryption_failed, operation=operation, status=CommonVariables.extension_error_status, code=str(CommonVariables.encryption_failed), message='Encryption config not found.') if __name__ == '__main__': main()