Skip to content

Commit

Permalink
Merge pull request #1363 from ancorgs/tpm_master
Browse files Browse the repository at this point in the history
New EncryptionMethod using fde-tools for TPM-based unlocking
  • Loading branch information
ancorgs authored Nov 2, 2023
2 parents 70f4fa7 + dea440c commit 87d3e01
Show file tree
Hide file tree
Showing 24 changed files with 1,376 additions and 102 deletions.
8 changes: 8 additions & 0 deletions package/yast2-storage-ng.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
-------------------------------------------------------------------
Thu Nov 2 13:52:11 UTC 2023 - Ancor Gonzalez Sosa <ancor@suse.com>

- Encryption method TpmFde to be used by Agama (and later by YaST)
for setting up LUKS2 devices that are unlocked during boot using
a TPM chip (gh#yast/yast-storage-ng#1088, related to bsc#1210512)
- 5.0.4

-------------------------------------------------------------------
Thu Oct 19 14:46:45 UTC 2023 - Ancor Gonzalez Sosa <ancor@suse.com>

Expand Down
10 changes: 5 additions & 5 deletions package/yast2-storage-ng.spec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#

Name: yast2-storage-ng
Version: 5.0.3
Version: 5.0.4
Release: 0
Summary: YaST2 - Storage Configuration
License: GPL-2.0-only OR GPL-3.0-only
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
147 changes: 147 additions & 0 deletions src/lib/y2storage/encryption_method/tpm_fde.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# 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.
#
# Check the documentation of fde-tools for further information.
# https://github.com/openSUSE/fde-tools
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 are important
# 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

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
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 87d3e01

Please sign in to comment.