Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New EncryptionMethod using fde-tools for TPM-based unlocking #1363

Merged
merged 9 commits into from Nov 2, 2023
Merged
8 changes: 4 additions & 4 deletions package/yast2-storage-ng.spec
Expand Up @@ -25,8 +25,8 @@ Url: https://github.com/yast/yast-storage-ng

Source: %{name}-%{version}.tar.bz2

# New Md size calculation
BuildRequires: libstorage-ng-ruby >= 4.4.76
# Encryption#use_key_file_in_commit
BuildRequires: libstorage-ng-ruby >= 4.5.144
BuildRequires: update-desktop-files
# Replace PackageSystem with Package
BuildRequires: yast2 >= 4.4.38
Expand All @@ -47,8 +47,8 @@ BuildRequires: rubygem(%{rb_default_ruby_abi}:parallel_tests)

# findutils for xargs
Requires: findutils
# New Md size calculation
Requires: libstorage-ng-ruby >= 4.4.76
# Encryption#use_key_file_in_commit
Requires: libstorage-ng-ruby >= 4.5.144
# Replace PackageSystem with Package
Requires: yast2 >= 4.4.38
# Y2Packager::Repository
Expand Down
8 changes: 5 additions & 3 deletions src/lib/y2partitioner/widgets/overview.rb
@@ -1,4 +1,4 @@
# Copyright (c) [2017-2022] SUSE LLC
# Copyright (c) [2017-2023] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -209,13 +209,15 @@ def valid_setup?
#
# As a side effect, it will ask the user to install missing packages.
#
# @see Y2Storage::UsedStorageFeatures
# @see Y2Storage::StorageFeature
#
# @return [Boolean]
def packages_installed?
return true if Yast::Mode.installation

pkgs = device_graph.actiongraph.used_features.pkg_list
features = device_graph.actiongraph.used_features
features.concat(device_graph.yast_commit_features)
pkgs = features.pkg_list
Y2Storage::PackageHandler.new(pkgs).install
end

Expand Down
32 changes: 27 additions & 5 deletions src/lib/y2storage/devicegraph.rb
@@ -1,4 +1,4 @@
# Copyright (c) [2017-2021] SUSE LLC
# Copyright (c) [2017-2023] SUSE LLC
#
# All Rights Reserved.
#
Expand Down Expand Up @@ -594,12 +594,19 @@ def finish_installation

# List of storage features used by the devicegraph
#
# Note this is used during system installation. In the installed system, the
# combination of Actiongraph#used_features and Devicegraph#yast_commit_features
# is used instead.
#
# By default, it returns the features associated to all devices and filesystems
# in the devicegraph. The required_only argument can be used to limit the result
# by excluding features associated to those filesystems that have no mount point.
# by excluding features that are not mandatory to produce a functional system. For
# example, it excludes features associated to those filesystems that have no mount
# point.
#
# @param required_only [Boolean] whether the result should only include those
# features that are mandatory (ie. associated to devices with a mount point)
# features that are mandatory (ie. associated to devices with a mount point or
# to devices that will be configured during the first boot of the new system)
# @return [StorageFeaturesList]
def used_features(required_only: false)
type =
Expand All @@ -609,7 +616,9 @@ def used_features(required_only: false)
Storage::UsedFeaturesDependencyType_SUGGESTED
end

StorageFeaturesList.from_bitfield(storage_used_features(type))
list = StorageFeaturesList.from_bitfield(storage_used_features(type))
list.concat(yast_commit_features)
list
end

# List of required (mandatory) storage features used by the devicegraph
Expand All @@ -626,7 +635,20 @@ def optional_used_features
all = storage_used_features(Storage::UsedFeaturesDependencyType_SUGGESTED)
required = storage_used_features(Storage::UsedFeaturesDependencyType_REQUIRED)
# Using binary XOR in those bit fields to calculate the difference
StorageFeaturesList.from_bitfield(all ^ required)
list = StorageFeaturesList.from_bitfield(all ^ required)
list.concat(yast_commit_features)
list
end

# List of features that correspond to aspects handled by Y2Storage (not coming
# from libstorage-ng) and that need to be present in the target system either during
# the storage commit phase or at a later stage. Ie. features needed in the target
# system to access the device or to finish its configuration.
#
# @return [StorageFeaturesList]
def yast_commit_features
features = encryptions.flat_map(&:commit_features).uniq
StorageFeaturesList.new(features)
end

private
Expand Down
25 changes: 25 additions & 0 deletions src/lib/y2storage/encryption.rb
Expand Up @@ -62,6 +62,23 @@ class Encryption < BlkDevice
storage_forward :key_file
storage_forward :key_file=

# @!method use_key_file_in_commit?
# Whether the information at {#key_file} is used in the commit phase of libstorage-ng
# (in case it contains a valid value).
#
# The default value is true, but it can be set to false in order to fill the third column
# of the crypttab file without actually affecting the creation of the device.
#
# @return [Boolean]
storage_forward :use_key_file_in_commit?

# @!method use_key_file_in_commit=(value)
#
# Sets the {#use_key_file_in_commit?} flag
#
# @param value [Boolean]
storage_forward :use_key_file_in_commit=

# @!attribute cipher
# The encryption cipher
#
Expand Down Expand Up @@ -345,6 +362,14 @@ def finish_installation
encryption_process&.finish_installation
end

# Features that must be supported in the target system to finish the encryption
# process
#
# @return [Array<YastFeature>]
def commit_features
encryption_process&.commit_features || []
end

# If the current mount_by is suitable, it does nothing.
#
# Otherwise, it assigns the best option from all the suitable ones
Expand Down
5 changes: 4 additions & 1 deletion src/lib/y2storage/encryption_method.rb
Expand Up @@ -20,6 +20,7 @@
require "y2storage/encryption_method/luks1"
require "y2storage/encryption_method/pervasive_luks2"
require "y2storage/encryption_method/luks2"
require "y2storage/encryption_method/tpm_fde"
require "y2storage/encryption_method/random_swap"
require "y2storage/encryption_method/protected_swap"
require "y2storage/encryption_method/secure_swap"
Expand Down Expand Up @@ -47,6 +48,8 @@ module EncryptionMethod
PERVASIVE_LUKS2 = PervasiveLuks2.new
# Instance of the Luks2 method to be always returned by the module
LUKS2 = Luks2.new
# Instance of the TpmFde method to be always returned by the module
TPM_FDE = TpmFde.new
# Instance of the RandomSwap method to be always returned by the module
RANDOM_SWAP = RandomSwap.new
# Instance of the ProtectedSwap method to be always returned by the module
Expand All @@ -57,7 +60,7 @@ module EncryptionMethod
# Sorted list of all the method instances
# @see .all
ALL = [
LUKS1, PERVASIVE_LUKS2, LUKS2, RANDOM_SWAP, PROTECTED_SWAP, SECURE_SWAP
LUKS1, PERVASIVE_LUKS2, LUKS2, TPM_FDE, RANDOM_SWAP, PROTECTED_SWAP, SECURE_SWAP
]
private_constant :ALL

Expand Down
149 changes: 149 additions & 0 deletions src/lib/y2storage/encryption_method/tpm_fde.rb
@@ -0,0 +1,149 @@
# 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.
# Copyright (c) [2019] SUSE LLC

require "yast"
require "y2storage/encryption_method/base"
require "y2storage/yast_feature"
require "y2storage/encryption_processes/tpm_fde_tools"

Yast.import "Mode"
Yast.import "Package"

module Y2Storage
module EncryptionMethod
# Encryption method that allows to encrypt a device using LUKS2 and configure the unlocking
# process via the system TPM using the fde-tools created by SUSE.
#
# This is a quite special encryption method due to the way the fde-tools work. First of all,
# if this method is used, it must be used at least for the root (/) filesystem and only
# additionally for some other devices.
class TpmFde < Base
def initialize
textdomain "storage"

super(:tpm_fde, _("TPM-Based Full Disk Encrytion"))
end

# @see Base#used_for?
#
# @todo Not sure if this would be possible at the end, since the exact way to setup the
# system using fde-tools is still changing too often. In any case, having a precise result
# for this method will only be relevant when implementing support for creating encrypted
# devices in an installed system. During installation returning always false is perfectly
# correct.
#
# @return [Boolean] false
def used_for?(_encryption)
# One candidate criteria (still waiting for some conversations with fde-toold developers)
# could be:
# encryption.type.is?(:luks2) && key_file == EncryptionProcesses::TpmFdeTools.key_file_name
false
end

# @see Base#available?
#
# In this initial implementation this always returns false because there important
ancorgs marked this conversation as resolved.
Show resolved Hide resolved
# limitations to use this in (Auto)YaST:
#
# - The current version of the inst-sys cannot talk to the TPM
# - There is still no corresponding UI in the Expert Partitioner
# - Some mechanism to ensure consistency (eg. checking all devices use the same recovery
# password) need to be introduced
# - The current implementation of the encryption method only covers system installation
# (with no support to add a new encrypted device to a system already using fde-tools)
#
# So far, the encryption method is implemented to be used by Agama (which doesn't honor
# the {#available?} method.
#
# @return [Boolean] false
def available?
false
end

# Whether both the target system and the product being installed meet the requisites
# to setup devices using this encryption method.
#
# The encryption method must be used at least for the root filesystem (eg. is not possible to
# use it for /var but not for /), but that can't hardly be controlled here. A separate
# validation that considers the whole devicegraph is needed.
#
# @return [Boolean]
def possible?
tpm_system? && tpm_product?
end

# Creates an encryption device for the given block device
#
# @param blk_device [Y2Storage::BlkDevice]
# @param dm_name [String]
#
# @return [Y2Storage::Encryption]
def create_device(blk_device, dm_name, label: "")
encryption_process.create_device(blk_device, dm_name, label: label)
end

# Special method used in the tests to reset all memoizations
def reset
ancorgs marked this conversation as resolved.
Show resolved Hide resolved
@tpm_present = nil
end

private

# @see Base#encryption_process
def encryption_process
EncryptionProcesses::TpmFdeTools.new(self)
end

# Whether the system is capable of using the encryption method
#
# @see #possible?
#
# @return [Boolean]
def tpm_system?
Y2Storage::Arch.new.efiboot? && tpm_present?
end

# Whether a TPM2 chip is present and working
#
# @see #possible?
#
# @return [Boolean]
def tpm_present?
return @tpm_present unless @tpm_present.nil?

@tpm_present = EncryptionProcesses::FdeTools.new.tpm_present?
end

# Whether the product being installed has the ability to configure the encryption method
#
# @see #possible?
#
# @return [Boolean]
def tpm_product?
# TODO: We should likely do some memoization of the result. But it is not clear when
# such memoization would be invalidated (eg. new packages available due to some change
# in selected product or to new repositories).

# Beware: apart from true and false, AvailableAll can return nil if things go wrong
!!Yast::Package.AvailableAll(YastFeature::ENCRYPTION_TPM_FDE.pkg_list)
end
end
end
end
12 changes: 11 additions & 1 deletion src/lib/y2storage/encryption_processes/base.rb
@@ -1,4 +1,4 @@
# Copyright (c) [2019] SUSE LLC
# Copyright (c) [2019-2023] SUSE LLC
#
# All Rights Reserved.
#
Expand All @@ -18,6 +18,7 @@
# find current contact information at www.suse.com.

require "yast"
require "y2storage/yast_feature"

require "abstract_method"

Expand Down Expand Up @@ -93,6 +94,15 @@ def crypt_options(_blk_device)
[]
end

# Features objects to describe the requirements to perform the commit phase
# and any subsequent operation (eg., initialization during the first boot) of
# the encryption procedure
#
# @return [Array<YastFeature>]
def commit_features
[]
end

private

# Open options with the format expected by the underlying tools (cryptsetup)
Expand Down