Skip to content

Commit

Permalink
Merge pull request #826 from openSUSE/tpm_yast
Browse files Browse the repository at this point in the history
[Service] Delegate TPM-based encryption to YaST
  • Loading branch information
ancorgs authored Nov 2, 2023
2 parents 72cbbbd + 878f97f commit c875a36
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 94 deletions.
6 changes: 1 addition & 5 deletions products.d/ALP-Dolomite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ software:
- alp_hardware
optional_patterns: null # no optional pattern shared
mandatory_packages:
- package: device-mapper # Apparently needed if devices at /dev/mapper are used at boot (eg. FDE)
- package: fde-tools # Needed for FDE with TPM, hardcoded here temporarily
archs: aarch64, x86_64
- package: libtss2-tcti-device0
- package: ppc64-diag # Needed for hardware-based installations
archs: ppc64
optional_packages: null
base_product: ALP-Dolomite

security:
tpm_luks_open: true
lsm: selinux
available_lsms:
# apparmor:
Expand All @@ -48,6 +43,7 @@ storage:
encryption:
method: luks2
pbkd_function: pbkdf2
tpm_luks_open: true
volumes:
- "/"
volume_templates:
Expand Down
8 changes: 2 additions & 6 deletions products.d/leap16.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,11 @@ software:
- alp-container_runtime
- alp_defaults
optional_patterns: null # no optional pattern shared
mandatory_packages:
- package: device-mapper # Apparently needed if devices at /dev/mapper are used at boot (eg. FDE)
- package: fde-tools # Needed for FDE with TPM, hardcoded here temporarily
archs: aarch64, x86_64
- package: libtss2-tcti-device0
mandatory_packages: null
optional_packages: null
base_product: Leap16

security:
tpm_luks_open: true
lsm: selinux
available_lsms:
# apparmor:
Expand All @@ -42,6 +37,7 @@ storage:
encryption:
method: luks2
pbkd_function: pbkdf2
tpm_luks_open: true
volumes:
- "/"
volume_templates:
Expand Down
7 changes: 7 additions & 0 deletions service/lib/agama/dbus/y2dir/manager/modules/Package.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ def Available(_package_name)
true
end

# Determines whether the package is available
#
# @todo Perform a real D-Bus call.
def AvailableAll(_package_names)
true
end

# Determines whether the package is available
#
# @see https://github.com/yast/yast-yast2/blob/b8cd178b7f341f6e3438782cb703f4a3ab0529ed/library/packages/src/modules/Package.rb#L121
Expand Down
106 changes: 24 additions & 82 deletions service/lib/agama/storage/finisher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ def possible_steps
[
SecurityStep.new(logger, security),
CopyFilesStep.new(logger),
StorageStep.new(logger),
BootloaderStep.new(logger),
TpmStep.new(logger, config),
IguanaStep.new(logger),
SnapshotsStep.new(logger),
CopyLogsStep.new(logger),
Expand Down Expand Up @@ -113,18 +113,6 @@ def run?
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 copy files from the inst-sys to the target system
Expand Down Expand Up @@ -196,6 +184,17 @@ def cio_ignore_finish
end
end

# Step to finish the Y2Storage configuration
class StorageStep < Step
def label
"Adjusting storage configuration"
end

def run
wfm_write("storage_finish")
end
end

# Step to configure the file-system snapshots
class SnapshotsStep < Step
def label
Expand Down Expand Up @@ -229,75 +228,6 @@ def run
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"
Expand Down Expand Up @@ -336,6 +266,18 @@ def root_mount_options

options.empty? ? "defaults" : options.join(",")
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
end
end
Expand Down
15 changes: 14 additions & 1 deletion service/lib/agama/storage/proposal_settings_reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,26 @@ def lvm_reader(settings, value)
# @param settings [Agama::Storage::ProposalSettings]
# @param encryption [Hash]
def encryption_reader(settings, encryption)
method = Y2Storage::EncryptionMethod.find(encryption.fetch("method", ""))
method =
if try_tpm_fde?(encryption)
Y2Storage::EncryptionMethod::TPM_FDE
else
Y2Storage::EncryptionMethod.find(encryption.fetch("method", ""))
end
pbkd_function = Y2Storage::PbkdFunction.find(encryption.fetch("pbkd_function", ""))

settings.encryption.method = method if method
settings.encryption.pbkd_function = pbkd_function if pbkd_function
end

# @param encryption [Hash]
# @return [Boolean]
def try_tpm_fde?(encryption)
return false unless encryption["tpm_luks_open"] == true

Y2Storage::EncryptionMethod::TPM_FDE.possible?
end

# @param settings [Agama::Storage::ProposalSettings]
# @param value [String]
def space_policy_reader(settings, value)
Expand Down
5 changes: 5 additions & 0 deletions service/package/rubygem-agama.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
-------------------------------------------------------------------
Thu Nov 2 14:00:01 UTC 2023 - Ancor Gonzalez Sosa <[email protected]>

- Delegate TPM-based encryption to YaST (gh#openSUSE/agama#826)

-------------------------------------------------------------------
Mon Oct 23 11:33:26 UTC 2023 - Imobach Gonzalez Sosa <[email protected]>

Expand Down
1 change: 1 addition & 0 deletions service/test/agama/storage/manager_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
expect(security).to receive(:write)
expect(copy_files).to receive(:run)
expect(bootloader_finish).to receive(:write)
expect(Yast::WFM).to receive(:CallFunction).with("storage_finish", ["Write"])
expect(Yast::WFM).to receive(:CallFunction).with("snapshots_finish", ["Write"])
expect(Yast::WFM).to receive(:CallFunction).with("copy_logs_finish", ["Write"])
expect(Yast::WFM).to receive(:CallFunction).with("umount_finish", ["Write"])
Expand Down

0 comments on commit c875a36

Please sign in to comment.