-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #445 from yast/mountlist
Write the Iguana mountlist
- Loading branch information
Showing
6 changed files
with
463 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
# frozen_string_literal: true | ||
|
||
# Copyright (c) [2023] SUSE LLC | ||
# | ||
# All Rights Reserved. | ||
# | ||
# This program is free software; you can redistribute it and/or modify it | ||
# under the terms of version 2 of the GNU General Public License as published | ||
# by the Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
# more details. | ||
# | ||
# You should have received a copy of the GNU General Public License along | ||
# with this program; if not, contact SUSE LLC. | ||
# | ||
# To contact SUSE LLC about this file by physical or electronic mail, you may | ||
# find current contact information at www.suse.com. | ||
|
||
require "yast" | ||
require "yast2/execute" | ||
require "yast2/systemd/service" | ||
require "bootloader/finish_client" | ||
require "y2storage/storage_manager" | ||
require "dinstaller/with_progress" | ||
require "dinstaller/helpers" | ||
require "abstract_method" | ||
|
||
module DInstaller | ||
module Storage | ||
# Auxiliary class to handle the last storage-related steps of the installation | ||
class Finisher | ||
include WithProgress | ||
include Helpers | ||
|
||
# Constructor | ||
def initialize(logger, config, security) | ||
@logger = logger | ||
@config = config | ||
@security = security | ||
end | ||
|
||
# Execute the final storage actions, reporting the progress | ||
def run | ||
steps = possible_steps.select(&:run?) | ||
start_progress(steps.size) | ||
|
||
on_target do | ||
steps.each do |step| | ||
progress.step(step.label) { step.run } | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
# @return [Logger] | ||
attr_reader :logger | ||
|
||
# @return [Config] | ||
attr_reader :config | ||
|
||
# @return [Security] | ||
attr_reader :security | ||
|
||
# All possible steps, that may or not need to be executed | ||
def possible_steps | ||
[ | ||
SecurityStep.new(logger, security), | ||
BootloaderStep.new(logger), | ||
TpmStep.new(logger, config), | ||
IguanaStep.new(logger), | ||
SnapshotsStep.new(logger), | ||
CopyLogsStep.new(logger), | ||
UnmountStep.new(logger) | ||
] | ||
end | ||
|
||
# Base class for the Finisher steps containing some shared logic | ||
class Step | ||
# Base constructor | ||
def initialize(logger) | ||
@logger = logger | ||
end | ||
|
||
# Whether this step must be executed | ||
def run? | ||
true | ||
end | ||
|
||
# @!method run | ||
# Executes the step | ||
abstract_method :run | ||
|
||
# @!method label | ||
# Sentence to describe the step in the progress report | ||
# @return [String] | ||
abstract_method :label | ||
|
||
private | ||
|
||
# @return [Logger] | ||
attr_reader :logger | ||
|
||
def wfm_write(function) | ||
Yast::WFM.CallFunction(function, ["Write"]) | ||
end | ||
|
||
# Representation on the staging devicegraph of the root mount point | ||
# | ||
# @return [Y2Storage::MountPoint] | ||
def root_mount_point | ||
staging_graph.mount_points.find(&:root?) | ||
end | ||
|
||
# @return [Y2Storage::Devicegraph] | ||
def staging_graph | ||
Y2Storage::StorageManager.instance.staging | ||
end | ||
end | ||
|
||
# Step to write the security settings | ||
class SecurityStep < Step | ||
# Constructor | ||
def initialize(logger, security) | ||
super(logger) | ||
@security = security | ||
end | ||
|
||
def label | ||
"Writing Linux Security Modules configuration" | ||
end | ||
|
||
def run | ||
@security.write | ||
end | ||
end | ||
|
||
# Step to write the bootloader configuration | ||
class BootloaderStep < Step | ||
def label | ||
"Installing bootloader" | ||
end | ||
|
||
def run | ||
::Bootloader::FinishClient.new.write | ||
end | ||
end | ||
|
||
# Step to configure the file-system snapshots | ||
class SnapshotsStep < Step | ||
def label | ||
"Configuring file systems snapshots" | ||
end | ||
|
||
def run | ||
wfm_write("snapshots_finish") | ||
end | ||
end | ||
|
||
# Step to copy the installation logs | ||
class CopyLogsStep < Step | ||
def label | ||
"Copying logs" | ||
end | ||
|
||
def run | ||
wfm_write("copy_logs_finish") | ||
end | ||
end | ||
|
||
# Step to unmount the target file-systems | ||
class UnmountStep < Step | ||
def label | ||
"Unmounting storage devices" | ||
end | ||
|
||
def run | ||
wfm_write("umount_finish") | ||
end | ||
end | ||
|
||
# Step to configure LUKS unlocking via TPMv2, if possible | ||
class TpmStep < Step | ||
# Constructor | ||
def initialize(logger, config) | ||
super(logger) | ||
@config = config | ||
end | ||
|
||
def label | ||
"Preparing the system to unlock the encryption using the TPM" | ||
end | ||
|
||
def run? | ||
tpm_product? && tpm_proposal? && tpm_system? | ||
end | ||
|
||
def run | ||
keyfile_path = File.join("root", ".root.keyfile") | ||
Yast::Execute.on_target!( | ||
"fdectl", "add-secondary-key", "--keyfile", keyfile_path, | ||
stdin: "#{luks.password}\n", | ||
recorder: Yast::ReducedRecorder.new(skip: :stdin) | ||
) | ||
|
||
service = Yast2::Systemd::Service.find("fde-tpm-enroll.service") | ||
logger.info "FDE: TPM enroll service: #{service}" | ||
service&.enable | ||
rescue Cheetah::ExecutionFailed | ||
false | ||
end | ||
|
||
private | ||
|
||
def tpm_proposal? | ||
!!luks | ||
end | ||
|
||
# LUKS device from the devicegraph | ||
# | ||
# @return [Y2Storage::Luks, nil] nil if the root mount point is not encrypted | ||
def luks | ||
root_mount_point.ancestors.find do |dev| | ||
dev.is?(:luks) | ||
end | ||
end | ||
|
||
def tpm_system? | ||
Y2Storage::Arch.new.efiboot? && tpm_present? | ||
end | ||
|
||
def tpm_present? | ||
return @tpm_present unless @tpm_present.nil? | ||
|
||
@tpm_present = | ||
begin | ||
Yast::Execute.on_target!("fdectl", "tpm-present") | ||
logger.info "FDE: TPMv2 detected" | ||
true | ||
rescue Cheetah::ExecutionFailed | ||
logger.info "FDE: TPMv2 not detected" | ||
false | ||
end | ||
end | ||
|
||
def tpm_product? | ||
@config.data.fetch("security", {}).fetch("tpm_luks_open", false) | ||
end | ||
end | ||
|
||
# Step to write the mountlist file for Iguana, if needed | ||
class IguanaStep < Step | ||
IGUANA_PATH = "/iguana" | ||
private_constant :IGUANA_PATH | ||
IGUANA_MOUNTLIST = File.join(IGUANA_PATH, "mountlist").freeze | ||
private_constant :IGUANA_MOUNTLIST | ||
|
||
def label | ||
"Configuring Iguana" | ||
end | ||
|
||
def run? | ||
File.directory?(IGUANA_PATH) | ||
end | ||
|
||
def run | ||
File.open(IGUANA_MOUNTLIST, "w") do |list| | ||
list.puts "#{root_device_name} /sysroot #{root_mount_options}" | ||
end | ||
end | ||
|
||
private | ||
|
||
def root_device_name | ||
fs = root_mount_point&.filesystem | ||
# This should never happen, we must have a root file-system | ||
return "" unless fs | ||
|
||
fs.respond_to?(:preferred_name) ? fs.preferred_name : fs.name | ||
end | ||
|
||
def root_mount_options | ||
options = root_mount_point&.mount_options | ||
# This should never happen, we must have a root mount point | ||
return "" unless options | ||
|
||
options.empty? ? "defaults" : options.join(",") | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.