Merge branch 'calamares' of github.com:calamares/calamares into calamares

This commit is contained in:
Adriaan de Groot 2023-06-25 22:05:28 +02:00
commit e78f8840ea
15 changed files with 139 additions and 46 deletions

View File

@ -17,16 +17,16 @@
> Calamares is a distribution-independent system installer, with an advanced partitioning
> feature for both manual and automated partitioning operations. Calamares is designed to
> be customizable by distribution maintainers without need for cumbersome patching,
> thanks to third party branding and external modules support.
> be customizable by distribution maintainers without the need for cumbersome patching,
> thanks to third-party branding and external modules support.
## Target Audience
Calamares is a Linux installer; users who install Linux on a computer will hopefully
use it just **once**, to install their Linux distribution. Calamares is not
a "ready to use" application: distributions apply a huge amount of customisation
a "ready to use" application: distributions apply a huge amount of customization
and configuration to Calamares, and the target audience for this repository
is those distributions, and the people who make those Linux distro's.
is those distributions, and the people who make those Linux distros.
Calamares has some [generic user documentation](https://calamares.io/docs/users-guide/)
for end-users, but most of what we have is for distro developers.
@ -45,10 +45,10 @@ The dependencies are explained in [CONTRIBUTING.md](CONTRIBUTING.md).
## Contributing to Calamares
Calamares welcomes PRs. New issues are welcome, too.
There are both the Calamares **core** repository (this one),
There are both the Calamares **core** repository (this one)
and an **extensions** repository ([Calamares extensions](https://github.com/calamares/calamares-extensions)).
Contributions to code, modules, documentation, the wiki and the website are all welcome.
Contributions to code, modules, documentation, the wiki, and the website are all welcome.
There is more information in the [CONTRIBUTING.md](CONTRIBUTING.md) file.
## Join the Conversation

View File

@ -197,6 +197,8 @@ desktop_environments = [
DesktopEnvironment('/usr/bin/sway', 'sway'),
DesktopEnvironment('/usr/bin/ukui-session', 'ukui'),
DesktopEnvironment('/usr/bin/cutefish-session', 'cutefish-xsession'),
DesktopEnvironment('/usr/bin/river', 'river'),
DesktopEnvironment('/usr/bin/Hyprland', 'hyprland'),
]

View File

@ -54,6 +54,7 @@ class cpuinfo(object):
self.number_of_cores = 0
cpu = self._cpuinfo()
if 'vendor_id' in cpu['proc0']:
self.is_intel = cpu['proc0']['vendor_id'].lower() == "genuineintel"
self.is_amd = cpu['proc0']['vendor_id'].lower() == "authenticamd"
self.number_of_cores = len(cpu)

View File

@ -9,7 +9,6 @@ calamares_add_plugin(luksbootkeyfile
SOURCES
LuksBootKeyFileJob.cpp
SHARED_LIB
NO_CONFIG
)
calamares_add_test(luksbootkeyfiletest SOURCES Tests.cpp LuksBootKeyFileJob.cpp)

View File

@ -135,34 +135,64 @@ generateTargetKeyfile()
}
static bool
setupLuks( const LuksDevice& d )
setupLuks( const LuksDevice& d, const QString& luks2Hash )
{
// Sometimes this error is thrown: "All key slots full"
// luksAddKey will fail. So, remove the first slot to make room
// Get luksDump for this device
auto luks_dump = CalamaresUtils::System::instance()->targetEnvCommand(
{ "cryptsetup", "luksDump", d.device }, QString(), QString(), std::chrono::seconds( 5 ) );
if ( luks_dump.getExitCode() == 0 )
{ QStringLiteral( "cryptsetup" ), QStringLiteral( "luksDump" ), d.device },
QString(),
QString(),
std::chrono::seconds( 5 ) );
if ( luks_dump.getExitCode() != 0 )
{
QRegularExpression re( QStringLiteral( R"(\d+:\s*enabled)" ), QRegularExpression::CaseInsensitiveOption );
int count = luks_dump.getOutput().count( re );
cDebug() << "Luks Dump slot count: " << count;
if ( count >= 7 )
cWarning() << "Could not get LUKS information on " << d.device << ':' << luks_dump.getOutput() << "(exit code"
<< luks_dump.getExitCode() << ')';
return false;
}
// Check LUKS version
int luks_version = 0;
QRegularExpression version_re( QStringLiteral( R"(version:\s*([0-9]))" ),
QRegularExpression::CaseInsensitiveOption );
QRegularExpressionMatch match = version_re.match( luks_dump.getOutput() );
if ( ! match.hasMatch() )
{
auto r = CalamaresUtils::System::instance()->targetEnvCommand(
{ "cryptsetup", "luksKillSlot", d.device, "1" }, QString(), d.passphrase, std::chrono::seconds( 60 ) );
if ( r.getExitCode() != 0 )
cWarning() << "Could not get LUKS version on device: " << d.device;
return false;
}
bool ok;
luks_version = match.captured(1).toInt(&ok);
if( ! ok )
{
cWarning() << "Could not kill a slot to make room on" << d.device << ':' << r.getOutput()
<< "(exit code" << r.getExitCode() << ')';
cWarning() << "Could not get LUKS version on device: " << d.device;
return false;
}
cDebug() << "LUKS" << luks_version << " found on device: " << d.device;
// Check the number of slots used for LUKS1 devices
if ( luks_version == 1 )
{
QRegularExpression slots_re( QStringLiteral( R"(\d+:\s*enabled)" ),
QRegularExpression::CaseInsensitiveOption );
if ( luks_dump.getOutput().count( slots_re ) == 8 )
{
cWarning() << "No key slots left on LUKS1 device: " << d.device;
return false;
}
}
}
// Adding the key can take some times, measured around 15 seconds with
// a HDD (spinning rust) and a slow-ish computer. Give it a minute.
// Add the key to the keyfile
QStringList args = { QStringLiteral( "cryptsetup" ), QStringLiteral( "luksAddKey" ), d.device, keyfile };
if ( luks_version == 2 && luks2Hash != QString() )
{
args.insert(2, "--pbkdf");
args.insert(3, luks2Hash);
}
auto r = CalamaresUtils::System::instance()->targetEnvCommand(
{ "cryptsetup", "luksAddKey", d.device, keyfile }, QString(), d.passphrase, std::chrono::seconds( 60 ) );
args,
QString(),
d.passphrase,
std::chrono::seconds( 60 ) );
if ( r.getExitCode() != 0 )
{
cWarning() << "Could not configure LUKS keyfile on" << d.device << ':' << r.getOutput() << "(exit code"
@ -259,15 +289,15 @@ LuksBootKeyFileJob::exec()
if ( it == s.devices.begin() )
{
// Then there was no root partition
cDebug() << Logger::SubEntry << "No root partition.";
// User has configured non-root partition for encryption
cDebug() << Logger::SubEntry << "No root partition, skipping keyfile creation.";
return Calamares::JobResult::ok();
}
// /boot partition is not encrypted, keyfile must not be used
// But only if root partition is not encrypted
if ( hasUnencryptedSeparateBoot() && !hasEncryptedRoot() )
{
// /boot partition is not encrypted, keyfile must not be used
// But only if root partition is not encrypted
cDebug() << Logger::SubEntry << "/boot partition is not encrypted, skipping keyfile creation.";
return Calamares::JobResult::ok();
}
@ -295,13 +325,22 @@ LuksBootKeyFileJob::exec()
continue;
}
if ( !setupLuks( d ) )
return Calamares::JobResult::error(
tr( "Encrypted rootfs setup error" ),
tr( "Could not configure LUKS key file on partition %1." ).arg( d.device ) );
if ( !setupLuks( d, m_luks2Hash ) )
{
// Could not configure the LUKS partition
// This should not stop the installation: do not return Calamares::JobResult::error.
cError() << "Encrypted rootfs setup error: could not configure LUKS key file on partition " << d.device;
}
}
return Calamares::JobResult::ok();
}
void
LuksBootKeyFileJob::setConfigurationMap( const QVariantMap& configurationMap )
{
m_luks2Hash = CalamaresUtils::getString(
configurationMap, QStringLiteral( "luks2Hash" ), QString() );
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( LuksBootKeyFileJobFactory, registerPlugin< LuksBootKeyFileJob >(); )

View File

@ -30,6 +30,11 @@ public:
QString prettyName() const override;
Calamares::JobResult exec() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
private:
QString m_luks2Hash;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( LuksBootKeyFileJobFactory )

View File

@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
# Luksbootkeyfile configuration. A key file is created for the
# LUKS encrypted devices.
---
# Set Password-Based Key Derivation Function (PBKDF) algorithm
# for LUKS keyslot.
#
# There are three usable values: pbkdf2, argon2i or argon2id.
#
# When not set, the cryptsetup default is used
#luks2Hash: argon2id

View File

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2023 Arjen Balfoort <arjenbalfoort@hotmail.com>
# SPDX-License-Identifier: GPL-3.0-or-later
---
$schema: https://json-schema.org/schema#
$id: https://calamares.io/schemas/luksbootkeyfile
additionalProperties: false
type: object
properties:
luks2Hash: { type: string, enum: [ pbkdf2, argon2i, argon2id ] }

View File

@ -282,12 +282,12 @@ class PMDnf(PackageManager):
backend = "dnf"
def install(self, pkgs, from_local=False):
check_target_env_call(["dnf", "-y", "install"] + pkgs)
check_target_env_call(["dnf-3", "-y", "install"] + pkgs)
def remove(self, pkgs):
# ignore the error code for now because dnf thinks removing a
# nonexistent package is an error
target_env_call(["dnf", "--disablerepo=*", "-C", "-y",
target_env_call(["dnf-3", "--disablerepo=*", "-C", "-y",
"remove"] + pkgs)
def update_db(self):
@ -295,7 +295,7 @@ class PMDnf(PackageManager):
pass
def update_system(self):
check_target_env_call(["dnf", "-y", "upgrade"])
check_target_env_call(["dnf-3", "-y", "upgrade"])
class PMDummy(PackageManager):

View File

@ -415,6 +415,7 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_requiredPartitionTableType = CalamaresUtils::getStringList( configurationMap, "requiredPartitionTableType" );
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
gs->insert( "armInstall", CalamaresUtils::getBool( configurationMap, "armInstall", false ) );
fillGSConfigurationEFI( gs, configurationMap );
fillConfigurationFSTypes( configurationMap );
}

View File

@ -443,7 +443,15 @@ runOsprober( DeviceModel* dm )
bool
isEfiSystem()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
if ( gs->contains( "armInstall" ) && gs->value( "armInstall" ).toBool() )
{
return true;
}
else
{
return QDir( "/sys/firmware/efi/efivars" ).exists();
}
}
bool

View File

@ -95,8 +95,16 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
// the logical sector size (usually 512B). EFI starts with 2MiB
// empty and a EFI boot partition, while BIOS starts at
// the 1MiB boundary (usually sector 2048).
int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB;
// ARM empty sectors are 16 MiB in size.
int empty_space_sizeB;
if ( gs->contains( "armInstall" ) && gs->value( "armInstall" ).toBool() )
{
empty_space_sizeB = 16_MiB;
}
else
{
empty_space_sizeB = isEfi ? 2_MiB : 1_MiB;
}
// Since sectors count from 0, if the space is 2048 sectors in size,
// the first free sector has number 2048 (and there are 2048 sectors
// before that one, numbered 0..2047).

View File

@ -124,6 +124,13 @@ initialPartitioningChoice: none
# one of the items from the options.
initialSwapChoice: none
# armInstall
#
# Leaves 16MB empty at the start of a drive when partitioning
# where usually the u-boot loader goes
#
# armInstall: false
# Default partition table type, used when a "erase" disk is made.
#
# When erasing a disk, a new partition table is created on disk.

View File

@ -14,6 +14,7 @@ properties:
userSwapChoices: { type: array, items: { type: string, enum: [ none, reuse, small, suspend, file ] } }
# ensureSuspendToDisk: { type: boolean, default: true } # Legacy
# neverCreateSwap: { type: boolean, default: false } # Legacy
armInstall: { type: boolean, default: false }
allowZfsEncryption: { type: boolean, default: true }
drawNestedPartitions: { type: boolean, default: false }

View File

@ -31,7 +31,7 @@
# # finally masks pacman-init (an ArchLinux-only service).
# #
# units:
# - name: "NetworkManager"
# - name: "NetworkManager.service"
# action: "enable"
# mandatory: true
#
@ -40,12 +40,12 @@
# # The property "mandatory" is taken to be false by default here
# # because it is not specified
#
# - name: "graphical"
# - name: "graphical.target"
# action: "enable"
# # The property "mandatory" is taken to be false by default here
# # because it is not specified
#
# - name: "pacman-init"
# - name: "pacman-init.service"
# action: "mask"
# # The property "mandatory" is taken to be false by default here
# # because it is not specified