diff --git a/SOURCES/storage-safemode-luks.diff b/SOURCES/storage-safemode-luks.diff new file mode 100644 index 0000000..fd78028 --- /dev/null +++ b/SOURCES/storage-safemode-luks.diff @@ -0,0 +1,602 @@ +diff --git a/library/blivet.py b/library/blivet.py +index cb48e71..e1903f3 100644 +--- a/library/blivet.py ++++ b/library/blivet.py +@@ -167,11 +167,16 @@ class BlivetBase(object): + raise NotImplementedError() + + def _manage_one_encryption(self, device): ++ global safe_mode + ret = device + # Make sure to handle adjusting both existing stacks and future stacks. + if device == device.raw_device and self._spec_dict['encryption']: + # add luks + luks_name = "luks-%s" % device._name ++ if safe_mode and (device.original_format.type is not None or ++ device.original_format.name != get_format(None).name): ++ raise BlivetAnsibleError("cannot remove existing formatting on device '%s' in safe mode due to adding encryption" % ++ device._name) + if not device.format.exists: + fmt = device.format + else: +@@ -196,6 +201,10 @@ class BlivetBase(object): + ret = luks_device + elif device != device.raw_device and not self._spec_dict['encryption']: + # remove luks ++ if safe_mode and (device.original_format.type is not None or ++ device.original_format.name != get_format(None).name): ++ raise BlivetAnsibleError("cannot remove existing formatting on device '%s' in safe mode due to encryption removal" % ++ device._name) + if not device.format.exists: + fmt = device.format + else: +@@ -823,17 +832,21 @@ class BlivetPool(BlivetBase): + + def manage(self): + """ Schedule actions to configure this pool according to the yaml input. """ ++ global safe_mode + # look up the device + self._look_up_disks() + self._look_up_device() + + # schedule destroy if appropriate, including member type change +- if not self.ultimately_present or self._member_management_is_destructive(): +- if not self.ultimately_present: +- self._manage_volumes() ++ if not self.ultimately_present: ++ self._manage_volumes() + self._destroy() +- if not self.ultimately_present: +- return ++ return ++ elif self._member_management_is_destructive(): ++ if safe_mode: ++ raise BlivetAnsibleError("cannot remove and recreate existing pool '%s' in safe mode" % self._pool['name']) ++ else: ++ self._destroy() + + # schedule create if appropriate + self._create() +diff --git a/tests/create-test-file.yml b/tests/create-test-file.yml +new file mode 100644 +index 0000000..d1091e2 +--- /dev/null ++++ b/tests/create-test-file.yml +@@ -0,0 +1,13 @@ ++# Create a file to be checked that it still exists and no data loss has occured. ++# To use: ++# - set testfile to a path under the mountpoint being tested ++# - include this file (create-test-file.yml) before executing the ++# operation to be tested ++# - execute the operation that could potentially result in a loss of ++# data in the filesystem where testfile is located ++# - include verify-data-preservation.yml ++ ++- name: create a file ++ file: ++ path: "{{ testfile }}" ++ state: touch +diff --git a/tests/tests_luks.yml b/tests/tests_luks.yml +index f93efe5..f733714 100644 +--- a/tests/tests_luks.yml ++++ b/tests/tests_luks.yml +@@ -2,8 +2,8 @@ + - hosts: all + become: true + vars: +- storage_safe_mode: false + mount_location: '/opt/test1' ++ testfile: "{{ mount_location }}/quux" + volume_size: '5g' + + tasks: +@@ -64,10 +64,47 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Remove the encryption layer ++ include_role: ++ name: storage ++ vars: ++ storage_volumes: ++ - name: foo ++ type: disk ++ disks: "{{ unused_disks }}" ++ mount_point: "{{ mount_location }}" ++ encryption: false ++ encryption_password: 'yabbadabbadoo' ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove existing ++ formatting.*in safe mode due to encryption removal') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing filesystem in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Remove the encryption layer + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_volumes: + - name: foo + type: disk +@@ -78,10 +115,47 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Add encryption to the volume ++ include_role: ++ name: storage ++ vars: ++ storage_volumes: ++ - name: foo ++ type: disk ++ disks: "{{ unused_disks }}" ++ mount_point: "{{ mount_location }}" ++ encryption: true ++ encryption_password: 'yabbadabbadoo' ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove existing ++ formatting.*in safe mode due to adding encryption') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing filesystem in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Add encryption to the volume + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_volumes: + - name: foo + type: disk +@@ -102,6 +176,7 @@ + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: partition +@@ -135,6 +210,7 @@ + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: partition +@@ -149,10 +225,51 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Remove the encryption layer ++ include_role: ++ name: storage ++ vars: ++ storage_pools: ++ - name: foo ++ type: partition ++ disks: "{{ unused_disks }}" ++ volumes: ++ - name: test1 ++ type: partition ++ mount_point: "{{ mount_location }}" ++ size: 4g ++ encryption: false ++ encryption_password: 'yabbadabbadoo' ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove existing ++ formatting.*in safe mode due to encryption removal') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing filesystem in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Remove the encryption layer + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: partition +@@ -167,6 +284,48 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Add encryption to the volume ++ include_role: ++ name: storage ++ vars: ++ storage_pools: ++ - name: foo ++ type: partition ++ disks: "{{ unused_disks }}" ++ volumes: ++ - name: test1 ++ type: partition ++ mount_point: "{{ mount_location }}" ++ size: 4g ++ encryption: true ++ encryption_password: 'yabbadabbadoo' ++ ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove existing ++ formatting.*in safe mode due to adding encryption') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing volume in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Test key file handling + block: + - name: Create a key file +@@ -186,6 +345,7 @@ + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: partition +@@ -216,6 +376,7 @@ + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: lvm +@@ -248,6 +409,7 @@ + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: lvm +@@ -264,10 +426,52 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Remove the encryption layer ++ include_role: ++ name: storage ++ vars: ++ storage_pools: ++ - name: foo ++ type: lvm ++ disks: "{{ unused_disks }}" ++ volumes: ++ - name: test1 ++ mount_point: "{{ mount_location }}" ++ size: 4g ++ encryption: false ++ encryption_password: 'yabbadabbadoo' ++ ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove existing ++ formatting.*in safe mode due to encryption removal') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing volume in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Remove the encryption layer + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: lvm +@@ -281,10 +485,52 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Add encryption to the volume ++ include_role: ++ name: storage ++ vars: ++ storage_pools: ++ - name: foo ++ type: lvm ++ disks: "{{ unused_disks }}" ++ volumes: ++ - name: test1 ++ mount_point: "{{ mount_location }}" ++ size: 4g ++ encryption: true ++ encryption_password: 'yabbadabbadoo' ++ ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove existing ++ formatting.*in safe mode due to adding encryption') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing volume in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Add encryption to the volume + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: lvm +diff --git a/tests/tests_luks_pool.yml b/tests/tests_luks_pool.yml +index b20b806..f44916f 100644 +--- a/tests/tests_luks_pool.yml ++++ b/tests/tests_luks_pool.yml +@@ -2,9 +2,10 @@ + - hosts: all + become: true + vars: +- storage_safe_mode: false + mount_location: '/opt/test1' + mount_location_2: '/opt/test2' ++ testfile: "{{ mount_location }}/quux" ++ testfile_location_2: "{{ mount_location_2 }}/quux" + volume_size: '5g' + + tasks: +@@ -92,10 +93,50 @@ + state: absent + changed_when: false + ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Remove the encryption layer ++ include_role: ++ name: storage ++ vars: ++ storage_pools: ++ - name: foo ++ type: lvm ++ disks: "{{ unused_disks }}" ++ encryption: false ++ encryption_password: 'yabbadabbadoo' ++ volumes: ++ - name: test1 ++ mount_point: "{{ mount_location }}" ++ size: 4g ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove and recreate existing ++ pool.*in safe mode') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing pool in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ + - name: Remove the encryption layer + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: lvm +@@ -109,10 +150,53 @@ + + - include_tasks: verify-role-results.yml + +- - name: Add encryption to the volume ++ - import_tasks: create-test-file.yml ++ ++ - name: Test for correct handling of safe_mode ++ block: ++ - name: Add encryption to the pool ++ include_role: ++ name: storage ++ vars: ++ storage_pools: ++ - name: foo ++ type: lvm ++ disks: "{{ unused_disks }}" ++ encryption: true ++ encryption_password: 'yabbadabbadoo' ++ encryption_luks_version: luks1 ++ encryption_key_size: 512 ++ encryption_cipher: 'serpent-xts-plain64' ++ volumes: ++ - name: test1 ++ mount_point: "{{ mount_location }}" ++ size: 4g ++ - name: unreachable task ++ fail: ++ msg: UNREACH ++ rescue: ++ - name: Check that we failed in the role ++ assert: ++ that: ++ - ansible_failed_result.msg != 'UNREACH' ++ msg: "Role has not failed when it should have" ++ ++ - name: Verify the output of the safe_mode test ++ assert: ++ that: "blivet_output.failed and ++ blivet_output.msg ++ |regex_search('cannot remove and recreate existing ++ pool.*in safe mode') ++ and not blivet_output.changed" ++ msg: "Unexpected behavior w/ existing pool in safe mode" ++ ++ - import_tasks: verify-data-preservation.yml ++ ++ - name: Add encryption to the pool + include_role: + name: storage + vars: ++ storage_safe_mode: false + storage_pools: + - name: foo + type: lvm +@@ -129,6 +213,8 @@ + + - include_tasks: verify-role-results.yml + ++ - import_tasks: create-test-file.yml ++ + - name: Change the mountpoint, leaving encryption in place + include_role: + name: storage +@@ -144,6 +230,10 @@ + mount_point: "{{ mount_location_2 }}" + size: 4g + ++ - import_tasks: verify-data-preservation.yml ++ vars: ++ testfile: "{{ testfile_location_2 }}" ++ + - include_tasks: verify-role-results.yml + + - name: Clean up +diff --git a/tests/verify-data-preservation.yml b/tests/verify-data-preservation.yml +new file mode 100644 +index 0000000..eed790f +--- /dev/null ++++ b/tests/verify-data-preservation.yml +@@ -0,0 +1,19 @@ ++# Verify that a file still exists and no data loss has occured. ++# To use: ++# - set testfile to a path under the mountpoint being tested ++# - include create-test-file.yml before executing the operation to be ++# tested ++# - execute the operation that could potentially result in a loss of ++# data in the filesystem where testfile is located ++# - include this file (verify-data-preservation.yml) ++ ++- name: stat the file ++ stat: ++ path: "{{ testfile }}" ++ register: stat_r ++ ++- name: assert file presence ++ assert: ++ that: ++ stat_r.stat.isreg is defined and stat_r.stat.isreg ++ msg: "data lost!" diff --git a/SPECS/rhel-system-roles.spec b/SPECS/rhel-system-roles.spec index a89c24a..6ff2510 100644 --- a/SPECS/rhel-system-roles.spec +++ b/SPECS/rhel-system-roles.spec @@ -5,7 +5,7 @@ Name: linux-system-roles %endif Summary: Set of interfaces for unified system management Version: 1.0 -Release: 19%{?dist} +Release: 20%{?dist} #Group: Development/Libraries License: GPLv3+ and MIT and BSD @@ -117,7 +117,7 @@ Patch31: timesync-tier1-tags.diff Patch52: network-permissions.diff Patch53: network-tier1-tags.diff -#Patch61: storage-safemode.diff +Patch61: storage-safemode-luks.diff Url: https://github.com/linux-system-roles/ BuildArch: noarch @@ -181,7 +181,7 @@ cd %{rolename6}-%{id6} %if "%{roleprefix}" != "linux-system-roles." %patch6 -p1 %endif -#%%patch61 -p1 +%patch61 -p1 cd .. # for some roles, the prefix change can be scripted - see below @@ -458,6 +458,10 @@ cp -p $RPM_BUILD_ROOT%{_datadir}/ansible/roles/%{roleprefix}certificate/README.m %license %{_datadir}/ansible/roles/%{roleprefix}certificate/LICENSE %changelog +* Tue Sep 22 2020 Pavel Cahyna - 1.0-20 +- storage: backport upstream PR #168 to prevent toggling encryption in safe mode, + as it is a destructive operation. Resolves rhbz#1881524 + * Mon Aug 24 2020 Pavel Cahyna - 1.0-19 - Rebase network role to latest upstream, resolves rhbz#1800627 Drop a downstream patch with a test workaround that is not needed anymore.