[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.
Partition* partition = nullptr;
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(
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() );
}
// 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 )
{
partition->setPartitionPath( m_device->deviceNode() + QStringLiteral( "/" )

View File

@ -17,6 +17,8 @@
#include "JobQueue.h"
#include "Settings.h"
#include <QProcess>
ZfsJob::ZfsJob( QObject* parent )
: Calamares::CppJob( parent )
{
@ -30,6 +32,56 @@ ZfsJob::prettyName() const
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
ZfsJob::exec()
{
@ -78,15 +130,20 @@ ZfsJob::exec()
continue;
}
// Create the zpool
// zfs doesn't wait for the devices so pause for 2 seconds to ensure we give time for the device files to be created
auto r
= system->runCommand( { "sh", "-c", "sleep 2 ; zpool create " + m_poolOptions + " " + m_poolName + " " + deviceName },
std::chrono::seconds( 10 ) );
if ( r.getExitCode() != 0 )
ZfsResult zfsResult;
if ( gs->contains( "encryptphrase" ) )
{
return Calamares::JobResult::error( tr( "zpool failure" ),
tr( "Failed to create zpool on " + deviceName.toLocal8Bit() ) );
zfsResult
= 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
@ -105,12 +162,12 @@ ZfsJob::exec()
// 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
r = system->runCommand( { "sh",
"-c",
"zfs create " + m_datasetOptions
+ " -o canmount=off -o mountpoint=" + datasetMap[ "mountpoint" ].toString()
+ " " + m_poolName + "/" + datasetMap[ "dsName" ].toString() },
std::chrono::seconds( 10 ) );
auto r = system->runCommand( { "sh",
"-c",
"zfs create " + m_datasetOptions + " -o canmount=off -o mountpoint="
+ datasetMap[ "mountpoint" ].toString() + " " + m_poolName + "/"
+ datasetMap[ "dsName" ].toString() },
std::chrono::seconds( 10 ) );
if ( r.getExitCode() != 0 )
{
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 ( !datasetList.isEmpty() )
{
Calamares::JobQueue::instance()->globalStorage()->insert( "zfs", datasetList );
gs->insert( "zfs", datasetList );
}
}

View File

@ -20,6 +20,11 @@
#include "DllMacro.h"
struct ZfsResult {
bool success;
QString failureMessage;
};
/** @brief Create zpools and zfs datasets
*
*/
@ -43,6 +48,20 @@ private:
QString m_datasetOptions;
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 )