VMEncryption/main/oscrypto/rhel_72_lvm/encryptstates/PatchBootSystemState.py (162 lines of code) (raw):
#!/usr/bin/env python
#
# VM Backup extension
#
# Copyright 2015 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.
#
# Requires Python 2.7+
#
import inspect
import os
import sys
from inspect import ismethod
from time import sleep
from OSEncryptionState import *
class PatchBootSystemState(OSEncryptionState):
def __init__(self, context):
super(PatchBootSystemState, self).__init__('PatchBootSystemState', context)
def should_enter(self):
self.context.logger.log("Verifying if machine should enter patch_boot_system state")
if not super(PatchBootSystemState, self).should_enter():
return False
self.context.logger.log("Performing enter checks for patch_boot_system state")
if not os.path.exists('/dev/mapper/osencrypt'):
return False
return True
def enter(self):
if not self.should_enter():
return
self.context.logger.log("Entering patch_boot_system state")
self.command_executor.Execute('systemctl restart lvm2-lvmetad', True)
self.command_executor.Execute('pvscan', True)
self.command_executor.Execute('vgcfgrestore -f /volumes.lvm rootvg', True)
self.command_executor.Execute('cryptsetup luksClose osencrypt', True)
self._find_bek_and_execute_action('_luks_open')
self.unmount_lvm_volumes()
self.command_executor.Execute('mount /dev/rootvg/rootlv /oldroot', True)
self.command_executor.Execute('mount /dev/rootvg/varlv /oldroot/var', True)
self.command_executor.Execute('mount /dev/rootvg/usrlv /oldroot/usr', True)
self.command_executor.Execute('mount /dev/rootvg/tmplv /oldroot/tmp', True)
self.command_executor.Execute('mount /dev/rootvg/homelv /oldroot/home', True)
self.command_executor.Execute('mount /dev/rootvg/optlv /oldroot/opt', True)
self.command_executor.Execute('mount /boot', False)
self.command_executor.Execute('mount --make-rprivate /', True)
self.command_executor.Execute('mkdir /oldroot/memroot', True)
self.command_executor.Execute('pivot_root /oldroot /oldroot/memroot', True)
self.command_executor.ExecuteInBash('for i in dev proc sys boot; do mount --move /memroot/$i /$i; done', True)
self.command_executor.ExecuteInBash('[ -e "/boot/luks" ]', True)
try:
self._modify_pivoted_oldroot()
except Exception as e:
self.command_executor.Execute('mount --make-rprivate /')
self.command_executor.Execute('pivot_root /memroot /memroot/oldroot')
self.command_executor.Execute('rmdir /oldroot/memroot')
self.command_executor.ExecuteInBash('for i in dev proc sys boot; do mount --move /oldroot/$i /$i; done')
raise
else:
self.command_executor.Execute('mount --make-rprivate /')
self.command_executor.Execute('pivot_root /memroot /memroot/oldroot')
self.command_executor.Execute('rmdir /oldroot/memroot')
self.command_executor.ExecuteInBash('for i in dev proc sys boot; do mount --move /oldroot/$i /$i; done')
extension_full_name = 'Microsoft.Azure.Security.' + CommonVariables.extension_name
self.command_executor.Execute('/bin/cp -ax' +
' /var/log/azure/{0}'.format(extension_full_name) +
' /oldroot/var/log/azure/{0}.Stripdown'.format(extension_full_name))
self.command_executor.ExecuteInBash('/bin/cp -ax' +
' /var/lib/azure_disk_encryption_config/os_encryption_markers/*' +
' /oldroot/var/lib/azure_disk_encryption_config/os_encryption_markers/',
True)
self.command_executor.Execute('touch /oldroot/var/lib/azure_disk_encryption_config/os_encryption_markers/PatchBootSystemState', True)
self.command_executor.Execute('umount /boot')
self.command_executor.Execute('umount /oldroot')
self.command_executor.Execute('systemctl restart waagent')
self.context.logger.log("Pivoted back into memroot successfully")
self.unmount_lvm_volumes()
def should_exit(self):
self.context.logger.log("Verifying if machine should exit patch_boot_system state")
return super(PatchBootSystemState, self).should_exit()
def unmount_lvm_volumes(self):
self.command_executor.Execute('swapoff -a', True)
self.command_executor.Execute('umount -a')
for mountpoint in ['/var', '/opt', '/tmp', '/home', '/usr']:
if self.command_executor.Execute('mountpoint /oldroot' + mountpoint) == 0:
self.unmount('/oldroot' + mountpoint)
if self.command_executor.Execute('mountpoint ' + mountpoint) == 0:
self.unmount(mountpoint)
self.unmount_var()
def unmount_var(self):
unmounted = False
while not unmounted:
self.command_executor.Execute('systemctl stop NetworkManager')
self.command_executor.Execute('systemctl stop rsyslog')
self.command_executor.Execute('systemctl stop systemd-udevd')
self.command_executor.Execute('systemctl stop systemd-journald')
self.command_executor.Execute('systemctl stop systemd-hostnamed')
self.command_executor.Execute('systemctl stop atd')
self.command_executor.Execute('systemctl stop postfix')
self.unmount('/var')
sleep(3)
if self.command_executor.Execute('mountpoint /var'):
unmounted = True
def unmount(self, mountpoint):
if mountpoint != '/var':
self.unmount_var()
if self.command_executor.Execute("mountpoint " + mountpoint):
return
proc_comm = ProcessCommunicator()
self.command_executor.Execute(command_to_execute="fuser -vm " + mountpoint,
raise_exception_on_failure=True,
communicator=proc_comm)
self.context.logger.log("Processes using {0}:\n{1}".format(mountpoint, proc_comm.stdout))
procs_to_kill = filter(lambda p: p.isdigit(), proc_comm.stdout.split())
procs_to_kill = reversed(sorted(procs_to_kill))
for victim in procs_to_kill:
if int(victim) == os.getpid():
self.context.logger.log("Restarting WALA before committing suicide")
self.context.logger.log("Current executable path: " + sys.executable)
self.context.logger.log("Current executable arguments: " + " ".join(sys.argv))
# Kill any other daemons that are blocked and would be executed after this process commits
# suicide
self.command_executor.Execute('systemctl restart atd')
os.chdir('/')
with open("/delete-lock.sh", "w") as f:
f.write("rm -f /var/lib/azure_disk_encryption_config/daemon_lock_file.lck\n")
self.command_executor.Execute('at -f /delete-lock.sh now + 1 minutes', True)
self.command_executor.Execute('at -f /restart-wala.sh now + 2 minutes', True)
self.command_executor.ExecuteInBash('pkill -f .*ForLinux.*handle.py.*daemon.*', True)
if int(victim) == 1:
self.context.logger.log("Skipping init")
continue
self.command_executor.Execute('kill -9 {0}'.format(victim))
self.command_executor.Execute('telinit u', True)
sleep(3)
self.command_executor.Execute('umount ' + mountpoint, True)
def _append_contents_to_file(self, contents, path):
with open(path, 'a') as f:
f.write(contents)
def _modify_pivoted_oldroot(self):
self.context.logger.log("Pivoted into oldroot successfully")
scriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
ademoduledir = os.path.join(scriptdir, '../../91ade')
dracutmodulesdir = '/lib/dracut/modules.d'
udevaderulepath = os.path.join(dracutmodulesdir, '91ade/50-udev-ade.rules')
proc_comm = ProcessCommunicator()
self.command_executor.Execute('cp -r {0} /lib/dracut/modules.d/'.format(ademoduledir), True)
udevadm_cmd = "udevadm info --attribute-walk --name={0}".format(self.rootfs_block_device)
self.command_executor.Execute(command_to_execute=udevadm_cmd, raise_exception_on_failure=True, communicator=proc_comm)
matches = re.findall(r'ATTR{partition}=="(.*)"', proc_comm.stdout)
if not matches:
raise Exception("Could not parse ATTR{partition} from udevadm info")
partition = matches[0]
sed_cmd = 'sed -i.bak s/ENCRYPTED_DISK_PARTITION/{0}/ "{1}"'.format(partition, udevaderulepath)
self.command_executor.Execute(command_to_execute=sed_cmd, raise_exception_on_failure=True)
self._append_contents_to_file('\nGRUB_CMDLINE_LINUX+=" rd.debug"\n',
'/etc/default/grub')
self._append_contents_to_file('\nadd_drivers+=" fuse vfat nls_cp437 nls_iso8859-1"\n',
'/etc/dracut.conf')
self._append_contents_to_file('\nadd_dracutmodules+=" crypt"\n',
'/etc/dracut.conf')
self.command_executor.ExecuteInBash("/usr/sbin/dracut -f -v --kver `grubby --default-kernel | sed 's|/boot/vmlinuz-||g'`", True)
self.command_executor.Execute('grub2-install --recheck --force {0}'.format(self.rootfs_disk), True)
self.command_executor.Execute('grub2-mkconfig -o /boot/grub2/grub.cfg', True)
def _luks_open(self, bek_path):
self.command_executor.Execute('mount /boot')
self.command_executor.Execute('cryptsetup luksOpen --header /boot/luks/osluksheader {0} osencrypt -d {1}'.format(self.rootfs_block_device,
bek_path),
raise_exception_on_failure=True)
def _find_bek_and_execute_action(self, callback_method_name):
callback_method = getattr(self, callback_method_name)
if not ismethod(callback_method):
raise Exception("{0} is not a method".format(callback_method_name))
bek_path = self.bek_util.get_bek_passphrase_file(self.encryption_config)
callback_method(bek_path)