[partition][zfs] Add support for zfs encryption

This commit is contained in:
dalto 2021-11-08 17:26:08 -06:00
parent 074941e2bd
commit 6da9bad272
3 changed files with 100 additions and 16 deletions

View File

@ -243,7 +243,8 @@ CreatePartitionDialog::getNewlyCreatedPartition()
// does so, to set up the partition for create-and-then-set-flags. // does so, to set up the partition for create-and-then-set-flags.
Partition* partition = nullptr; Partition* partition = nullptr;
QString luksPassphrase = m_ui->encryptWidget->passphrase(); QString luksPassphrase = m_ui->encryptWidget->passphrase();
if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty() ) if ( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty()
&& fsType != FileSystem::Zfs )
{ {
partition = KPMHelpers::createNewEncryptedPartition( partition = KPMHelpers::createNewEncryptedPartition(
m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() ); m_parent, *m_device, m_role, fsType, fsLabel, first, last, luksPassphrase, PartitionTable::Flags() );
@ -254,6 +255,13 @@ CreatePartitionDialog::getNewlyCreatedPartition()
m_parent, *m_device, m_role, fsType, fsLabel, first, last, PartitionTable::Flags() ); m_parent, *m_device, m_role, fsType, fsLabel, first, last, PartitionTable::Flags() );
} }
// For zfs, we let the zfs module handle the encryption but we need to make the passphrase available to that module
if( m_ui->encryptWidget->state() == EncryptWidget::Encryption::Confirmed && !luksPassphrase.isEmpty()
&& fsType == FileSystem::Zfs )
{
Calamares::JobQueue::instance()->globalStorage()->insert( "encryptphrase", luksPassphrase );
}
if ( m_device->type() == Device::Type::LVM_Device ) if ( m_device->type() == Device::Type::LVM_Device )
{ {
partition->setPartitionPath( m_device->deviceNode() + QStringLiteral( "/" ) partition->setPartitionPath( m_device->deviceNode() + QStringLiteral( "/" )

View File

@ -17,6 +17,8 @@
#include "JobQueue.h" #include "JobQueue.h"
#include "Settings.h" #include "Settings.h"
#include <QProcess>
ZfsJob::ZfsJob( QObject* parent ) ZfsJob::ZfsJob( QObject* parent )
: Calamares::CppJob( parent ) : Calamares::CppJob( parent )
{ {
@ -30,6 +32,56 @@ ZfsJob::prettyName() const
return tr( "Create ZFS pools and datasets" ); return tr( "Create ZFS pools and datasets" );
} }
ZfsResult
ZfsJob::CreateZpool( QString deviceName, QString poolName, QString poolOptions, bool encrypt, QString passphrase ) const
{
// zfs doesn't wait for the devices so pause for 2 seconds to ensure we give time for the device files to be created
QString command;
if ( encrypt )
{
command = "sleep 2 ; echo \"" + passphrase + "\" | zpool create " + poolOptions
+ " -O encryption=aes-256-gcm -O keyformat=passphrase " + poolName + " " + deviceName;
}
else
{
command = "sleep 2 ; zpool create " + poolOptions + " " + poolName + " " + deviceName;
}
// We use a qProcess instead of runCommand so the password will not end up in the logs
QProcess process;
process.setProcessChannelMode( QProcess::MergedChannels );
cDebug() << Logger::SubEntry << "Running zpool create";
process.start( "sh", QStringList() << "-c" << command );
if ( !process.waitForStarted() )
{
return { false, tr( "zpool create process failed to start" ) };
}
if ( !process.waitForFinished( 5000 ) )
{
return { false, tr( "Process for zpool create timed out" ) };
}
QString output = QString::fromLocal8Bit( process.readAllStandardOutput() ).trimmed();
if ( process.exitStatus() == QProcess::CrashExit )
{
return { false, tr( "The output from the crash was: " ) + output };
}
auto exitcode = process.exitCode();
if ( exitcode != 0 )
{
cWarning() << "Failed to run zpool create. The output was: " + output;
return { false, tr( "Failed to create zpool on " ) + deviceName };
}
return { true, QString() };
}
Calamares::JobResult Calamares::JobResult
ZfsJob::exec() ZfsJob::exec()
{ {
@ -78,15 +130,20 @@ ZfsJob::exec()
continue; continue;
} }
// Create the zpool ZfsResult zfsResult;
// zfs doesn't wait for the devices so pause for 2 seconds to ensure we give time for the device files to be created if ( gs->contains( "encryptphrase" ) )
auto r
= system->runCommand( { "sh", "-c", "sleep 2 ; zpool create " + m_poolOptions + " " + m_poolName + " " + deviceName },
std::chrono::seconds( 10 ) );
if ( r.getExitCode() != 0 )
{ {
return Calamares::JobResult::error( tr( "zpool failure" ), zfsResult
tr( "Failed to create zpool on " + deviceName.toLocal8Bit() ) ); = CreateZpool( deviceName, m_poolName, m_poolOptions, true, gs->value( "encryptphrase" ).toString() );
}
else
{
zfsResult = CreateZpool( deviceName, m_poolName, m_poolOptions, false );
}
if ( !zfsResult.success )
{
return Calamares::JobResult::error( tr( "Failed to create zpool" ), zfsResult.failureMessage );
} }
// Create the datasets // Create the datasets
@ -105,12 +162,12 @@ ZfsJob::exec()
// Create the dataset. We set canmount=no regardless of the setting for now. // Create the dataset. We set canmount=no regardless of the setting for now.
// It is modified to the correct value in the mount module to ensure mount order is maintained // It is modified to the correct value in the mount module to ensure mount order is maintained
r = system->runCommand( { "sh", auto r = system->runCommand( { "sh",
"-c", "-c",
"zfs create " + m_datasetOptions "zfs create " + m_datasetOptions + " -o canmount=off -o mountpoint="
+ " -o canmount=off -o mountpoint=" + datasetMap[ "mountpoint" ].toString() + datasetMap[ "mountpoint" ].toString() + " " + m_poolName + "/"
+ " " + m_poolName + "/" + datasetMap[ "dsName" ].toString() }, + datasetMap[ "dsName" ].toString() },
std::chrono::seconds( 10 ) ); std::chrono::seconds( 10 ) );
if ( r.getExitCode() != 0 ) if ( r.getExitCode() != 0 )
{ {
cWarning() << "Failed to create dataset" << datasetMap[ "dsName" ].toString(); cWarning() << "Failed to create dataset" << datasetMap[ "dsName" ].toString();
@ -124,7 +181,7 @@ ZfsJob::exec()
// If the list isn't empty, add it to global storage // If the list isn't empty, add it to global storage
if ( !datasetList.isEmpty() ) if ( !datasetList.isEmpty() )
{ {
Calamares::JobQueue::instance()->globalStorage()->insert( "zfs", datasetList ); gs->insert( "zfs", datasetList );
} }
} }

View File

@ -20,6 +20,11 @@
#include "DllMacro.h" #include "DllMacro.h"
struct ZfsResult {
bool success;
QString failureMessage;
};
/** @brief Create zpools and zfs datasets /** @brief Create zpools and zfs datasets
* *
*/ */
@ -43,6 +48,20 @@ private:
QString m_datasetOptions; QString m_datasetOptions;
QList<QVariant> m_datasets; QList<QVariant> m_datasets;
/** @brief Creates a zpool based on the provided arguments
*
* Creates a zpool
* @p deviceName is a full path to the device the zpool should be created on
* @p poolName is a string containing the name of the pool to create
* @p poolOptions are the options to pass to zpool create
* @p encrypt is a boolean which determines if the pool should be encrypted
* @p passphrase is a string continaing the passphrase
*
*/
ZfsResult CreateZpool(QString deviceName, QString poolName, QString poolOptions, bool encrypt, QString passphrase = QString() ) const;
}; };
CALAMARES_PLUGIN_FACTORY_DECLARATION( ZfsJobFactory ) CALAMARES_PLUGIN_FACTORY_DECLARATION( ZfsJobFactory )