Skip to content

Commit

Permalink
Merge 5ce2240 into 8d85668
Browse files Browse the repository at this point in the history
  • Loading branch information
wfeldt committed Sep 17, 2019
2 parents 8d85668 + 5ce2240 commit 644c650
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 12 deletions.
10 changes: 10 additions & 0 deletions doc/boot-requirements.md
Expand Up @@ -66,6 +66,14 @@
- **does not require any partition (PReP will be reused and Grub2 can handle this setup)**
- and it is not on the boot disk
- **requires only a new PReP partition (to allocate Grub2)**
- with an encrypted proposal using LUKS2
- if there are no suitable PReP partitions in the target disk
- **requires a new PReP and a new /boot partition**
- if there is already a suitable PReP partition in the disk
- and it is on the boot disk
- **requires a /boot partition**
- and it is not on the boot disk
- **requires a new PReP and a new /boot partition**
- in bare metal (PowerNV)
- with a partitions-based proposal
- **does not require any booting partition (no Grub stage1, PPC firmware parses grub2.cfg)**
Expand Down Expand Up @@ -274,6 +282,8 @@
- **does not require any particular volume**
- in an encrypted proposal
- **does not require any particular volume**
- in an encrypted proposal using LUKS2
- **requires a new /boot partition to install Grub into it**
- with a MBR gap too small to accommodate Grub
- in a partitions-based proposal
- if the file-system selected for / can embed grub (ext2/3/4 or btrfs)
Expand Down
32 changes: 26 additions & 6 deletions src/lib/y2storage/boot_requirements_strategies/analyzer.rb
Expand Up @@ -20,6 +20,7 @@
require "yast"
require "pathname"
require "y2storage/planned"
require "y2storage/encryption_type"

module Y2Storage
module BootRequirementsStrategies
Expand Down Expand Up @@ -250,6 +251,15 @@ def boot_filesystem_type
filesystem_type(device_for_boot)
end

# Encryption type of boot device
#
# The device can be a planned one or filesystem from the devicegraph.
#
# @return [Y2Storage::EncryptionType] Encryption type
def boot_encryption_type
encryption_type(device_for_boot)
end

# Whether the partition table of the disk used for booting matches the
# given type.
#
Expand Down Expand Up @@ -533,15 +543,25 @@ def in_bcache?(device)
# The device can be a planned one or filesystem from the devicegraph.
#
# @param device [Filesystems::Base, Planned::Device, nil]
# @return [Boolean] false if device is nil
# @return [Boolean] device encryption state; return false if device is nil
def encrypted?(device)
return false if device.nil?
!encryption_type(device).is?(:none)
end

# Encryption type of device
#
# The device can be a planned one or filesystem from the devicegraph.
#
# Note: returns EncryptionType::LUKS1 for Planned::Device as there's no encryption type.
#
# @param device [Filesystems::Base, Planned::Device, nil]
# @return [Y2Storage::EncryptionType] Encryption type
def encryption_type(device)
if device.is_a?(Planned::Device)
device.respond_to?(:encrypt?) && device.encrypt?
else
device.plain_blk_devices.any? { |d| d.respond_to?(:encrypted?) && d.encrypted? }
end
(device.respond_to?(:encrypt?) && device.encrypt?) ? Y2Storage::EncryptionType::LUKS1 : nil
elsif device.respond_to?(:plain_blk_devices)
device.plain_blk_devices.map { |d| d.encryption&.type }.compact.first
end || Y2Storage::EncryptionType::NONE
end

# Whether the device is in a software RAID
Expand Down
25 changes: 22 additions & 3 deletions src/lib/y2storage/boot_requirements_strategies/base.rb
Expand Up @@ -45,7 +45,7 @@ class Base
:root_in_lvm?, :root_in_software_raid?, :encrypted_root?, :btrfs_root?,
:root_fs_can_embed_grub?, :boot_in_lvm?,
:boot_in_thin_lvm?, :boot_in_bcache?, :boot_in_software_raid?, :encrypted_boot?,
:boot_fs_can_embed_grub?, :boot_filesystem_type, :boot_can_embed_grub?,
:boot_fs_can_embed_grub?, :boot_filesystem_type, :boot_encryption_type,
:esp_in_lvm?, :esp_in_software_raid?, :esp_in_software_raid1?, :encrypted_esp?

# Constructor
Expand Down Expand Up @@ -81,7 +81,18 @@ def needed_partitions(target)
#
# @return [Array<SetupError>]
def warnings
[]
res = []

if !encrypted_for_grub?
error_message =
_(
"The boot loader cannot access the file system mounted at /boot. " \
"Only LUKS1 encryption is supported."
)
res << SetupError.new(message: error_message)
end

res
end

# All fatal boot errors detected in the setup, for example, when a / partition
Expand Down Expand Up @@ -136,7 +147,7 @@ def root_filesystem_missing?
end

def boot_partition_needed?
false
!encrypted_for_grub?
end

def too_small_boot?
Expand Down Expand Up @@ -201,6 +212,14 @@ def unknown_boot_disk_error
error_message = _("Boot requirements cannot be determined because there is no '/' mount point")
SetupError.new(message: error_message)
end

# Whether the boot device is encrypted and grub can decrypt it
#
# @return [Boolean] true if grub can decrypt boot device (or it is unencrypted)
def encrypted_for_grub?
t = boot_encryption_type
t.is?(:none) || t.is?(:luks1)
end
end
end
end
2 changes: 1 addition & 1 deletion src/lib/y2storage/boot_requirements_strategies/legacy.rb
Expand Up @@ -154,7 +154,7 @@ def mbr_gap_for_grub?
#
# @return [Boolean] true if a separate boot partition is needed, else false
def boot_partition_needed?
boot_ptable_type?(:msdos) && !mbr_gap_for_grub? && !root_can_embed_grub?
super || (boot_ptable_type?(:msdos) && !mbr_gap_for_grub? && !root_can_embed_grub?)
end

# @return [VolumeSpecification]
Expand Down
2 changes: 1 addition & 1 deletion src/lib/y2storage/boot_requirements_strategies/prep.rb
Expand Up @@ -98,7 +98,7 @@ def boot_partition_needed?

# We cannot ensure the mentioned firmware can handle technologies like
# LVM, MD or LUKS, so propose a separate /boot partition for those cases
root_in_lvm? || root_in_software_raid? || encrypted_root?
super || (root_in_lvm? || root_in_software_raid? || encrypted_root?)
end

def prep_partition_needed?
Expand Down
4 changes: 3 additions & 1 deletion test/support/boot_requirements_context.rb
Expand Up @@ -63,7 +63,8 @@ def find_vol(mount_point, volumes)
esp_in_lvm?: false,
esp_in_software_raid?: false,
esp_in_software_raid1?: false,
encrypted_esp?: false
encrypted_esp?: false,
boot_encryption_type: boot_enc_type
)
end

Expand All @@ -78,6 +79,7 @@ def find_vol(mount_point, volumes)
use_btrfs ? Y2Storage::Filesystems::Type::BTRFS : Y2Storage::Filesystems::Type::EXT4
end
let(:boot_ptable_type) { :msdos }
let(:boot_enc_type) { Y2Storage::EncryptionType::NONE }

# Mocks for Raspberry Pi detection
let(:raspi_system) { false }
Expand Down
46 changes: 46 additions & 0 deletions test/y2storage/boot_requirements_checker_ppc_test.rb
Expand Up @@ -83,6 +83,44 @@
end
end

RSpec.shared_examples "PReP + /boot partition" do
context "if there are no suitable PReP partitions in the target disk" do
let(:prep_partitions) { [] }

it "requires a new PReP and a new /boot partition" do
expect(checker.needed_partitions).to contain_exactly(
an_object_having_attributes(mount_point: nil, partition_id: prep_id),
an_object_having_attributes(mount_point: "/boot")
)
end
end

context "if there is already a suitable PReP partition in the disk" do
let(:prep_partitions) { [prep_partition] }

context "and it is on the boot disk" do
let(:boot_disk) { dev_sda }

it "requires a /boot partition" do
expect(checker.needed_partitions).to contain_exactly(
an_object_having_attributes(mount_point: "/boot")
)
end
end

context "and it is not on the boot disk" do
let(:boot_disk) { dev_sdb }

it "requires a new PReP and a new /boot partition" do
expect(checker.needed_partitions).to contain_exactly(
an_object_having_attributes(mount_point: nil, partition_id: prep_id),
an_object_having_attributes(mount_point: "/boot")
)
end
end
end
end

context "in a non-PowerNV system (KVM/LPAR)" do
let(:power_nv) { false }

Expand All @@ -101,6 +139,14 @@
let(:use_encryption) { true }
include_examples "PReP partition"
end

context "with an encrypted proposal using LUKS2" do
let(:use_lvm) { false }
let(:use_encryption) { true }
let(:boot_enc_type) { Y2Storage::EncryptionType::LUKS2 }

include_examples "PReP + /boot partition"
end
end

context "in bare metal (PowerNV)" do
Expand Down
8 changes: 8 additions & 0 deletions test/y2storage/boot_requirements_checker_x86_test.rb
Expand Up @@ -159,6 +159,14 @@

include_examples("needs no volume")
end

context "in an encrypted proposal using LUKS2" do
let(:use_lvm) { false }
let(:use_encryption) { true }
let(:boot_enc_type) { Y2Storage::EncryptionType::LUKS2 }

include_examples("needs /boot partition")
end
end

context "with a MBR gap too small to accommodate Grub" do
Expand Down
21 changes: 21 additions & 0 deletions test/y2storage/boot_requirements_errors_test.rb
Expand Up @@ -36,6 +36,9 @@
.and_return(use_thin_lvm)
allow_any_instance_of(Y2Storage::BootRequirementsStrategies::Analyzer).to receive(:boot_in_bcache?)
.and_return(use_bcache)
allow_any_instance_of(Y2Storage::BootRequirementsStrategies::Analyzer)
.to receive(:boot_encryption_type)
.and_return(enc_type)
end

let(:storage_arch) { instance_double(Storage::Arch) }
Expand All @@ -44,6 +47,7 @@
let(:efiboot) { false }
let(:use_thin_lvm) { false }
let(:use_bcache) { false }
let(:enc_type) { Y2Storage::EncryptionType::NONE }

let(:scenario) { "trivial" }

Expand Down Expand Up @@ -302,6 +306,23 @@ def format_zipl(name)
end
end

context "when /boot is encrypted" do
context "and grub can decrypt it" do
let(:enc_type) { Y2Storage::EncryptionType::LUKS1 }
it "does not contain any warning" do
expect(checker.warnings).to be_empty
end
end

context "and grub cannot decrypt it" do
let(:enc_type) { Y2Storage::EncryptionType::LUKS2 }
it "shows a warning that grub cannot access /boot" do
expect(checker.warnings.size).to eq(1)
expect(checker.warnings.first.message).to match(/cannot access/)
end
end
end

context "in a x86 system" do
let(:architecture) { :x86 }

Expand Down
58 changes: 58 additions & 0 deletions test/y2storage/boot_requirements_strategies/analyzer_test.rb
Expand Up @@ -767,4 +767,62 @@ def create_filesystem(device_name, mount_point)
end
end
end

describe "#boot_encryption_type" do
subject(:analyzer) { described_class.new(devicegraph, planned_devs, boot_name) }

context "if '/boot' is a planned plain partition" do
let(:planned_boot) { planned_partition(mount_point: "/boot") }

it "returns type none" do
expect(analyzer.boot_encryption_type).to eq Y2Storage::EncryptionType::NONE
end
end

context "if '/boot' is a planned encrypted partition" do
let(:planned_boot) { planned_partition(mount_point: "/boot", encryption_password: "12345678") }

it "returns type luks1" do
expect(analyzer.boot_encryption_type).to eq Y2Storage::EncryptionType::LUKS1
end
end

context "if '/boot' is a planned encrypted logical volume" do
let(:planned_boot) { planned_lv(mount_point: "/boot", encryption_password: "12345678") }

it "returns type luks1" do
expect(analyzer.boot_encryption_type).to eq Y2Storage::EncryptionType::LUKS1
end
end

context "if '/boot' is a plain partition from the devicegraph" do
let(:planned_devs) { [] }
let(:scenario) { "mixed_disks" }

it "returns type none" do
expect(analyzer.boot_encryption_type).to eq Y2Storage::EncryptionType::NONE
end
end

context "if '/boot' is an encrypted partition from the devicegraph with default encryption" do
let(:planned_devs) { [] }
let(:scenario) { "output/empty_hard_disk_gpt_50GiB-enc" }

it "returns type luks1" do
expect(analyzer.boot_encryption_type).to eq Y2Storage::EncryptionType::LUKS1
end
end

context "if '/boot' is an encrypted partition with encryption type luks2" do
let(:planned_devs) { [] }
let(:scenario) { "output/empty_hard_disk_gpt_50GiB-enc" }
before do
fake_devicegraph.find_by_name("/dev/sda2").encryption.type = Y2Storage::EncryptionType::LUKS2
end

it "returns type luks2" do
expect(analyzer.boot_encryption_type).to eq Y2Storage::EncryptionType::LUKS2
end
end
end
end

0 comments on commit 644c650

Please sign in to comment.