Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions nix/modules/lanzaboote.nix
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ in
'';
};

keepCurrentBootedConfiguration = lib.mkEnableOption "keeping the current booted configuration (labeled 'SAFE' in boot menu)";

pkiBundle = lib.mkOption {
type = lib.types.nullOr lib.types.externalPath;
description = "PKI bundle containing db, PK, KEK";
Expand Down Expand Up @@ -170,15 +172,15 @@ in
--systemd-boot-loader-config ${loaderConfigFile} \
--configuration-limit ${toString configurationLimit} \
--allow-unsigned ${lib.boolToString cfg.allowUnsigned} \
--bootcounting-initial-tries ${toString cfg.bootCounting.initialTries}'';
--bootcounting-initial-tries ${toString cfg.bootCounting.initialTries}${lib.optionalString cfg.keepCurrentBootedConfiguration " \\\n --safe-generation /run/booted-system"}'';
defaultText = lib.literalExpression ''
''${lib.getExe config.boot.lanzaboote.package} install \
--system ''${config.boot.kernelPackages.stdenv.hostPlatform.system} \
--systemd ''${config.systemd.package} \
--systemd-boot-loader-config ''${loaderConfigFile} \
--configuration-limit ''${toString configurationLimit} \
--allow-unsigned ''${lib.boolToString config.boot.lanzaboote.allowUnsigned} \
--bootcounting-initial-tries ''${toString config.boot.lanzaboote.bootCounting.initialTries}'';
--bootcounting-initial-tries ''${toString config.boot.lanzaboote.bootCounting.initialTries}''${lib.optionalString config.boot.lanzaboote.keepCurrentBootedConfiguration " \\\n --safe-generation /run/booted-system"}'';
};

extraEfiSysMountPoints = lib.mkOption {
Expand Down
37 changes: 29 additions & 8 deletions rust/tool/shared/src/generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ impl From<bootspec::BootJson> for LanzabooteExtension {
pub struct Generation {
/// Profile symlink index
pub version: u64,
// Custom label, replacing version if present
pub label: Option<String>,
/// Build time
pub build_time: Option<Date>,
/// Top-level specialisation name
Expand Down Expand Up @@ -93,6 +95,7 @@ impl Generation {
) -> Result<Self> {
Ok(Self {
version: link.version,
label: link.label.clone(),
build_time: link.build_time,
specialisation_name: Some(specialisation_name),
spec: ExtendedBootJson {
Expand Down Expand Up @@ -128,6 +131,7 @@ impl Generation {

Ok(Self {
version: link.version,
label: link.label.clone(),
build_time: link.build_time,
specialisation_name,
spec: ExtendedBootJson {
Expand Down Expand Up @@ -160,23 +164,30 @@ impl Generation {
.map(|x| x.to_string())
.unwrap_or_else(|| String::from("Unknown"));

format!(
"Generation {}{}, {}",
self.version,
self.describe_specialisation(),
build_time
)
format!("Generation {}, {}", self.version_tag(), build_time)
}

/// A unique short identifier.
pub fn version_tag(&self) -> String {
format!("{}{}", self.version, self.describe_specialisation(),)
format!(
"{}{}",
self.label
.clone()
.unwrap_or_else(|| self.version.to_string()),
self.describe_specialisation(),
)
}
}

impl fmt::Display for Generation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.version)
write!(
f,
"{}",
self.label
.clone()
.unwrap_or_else(|| self.version.to_string())
)
}
}

Expand All @@ -193,6 +204,7 @@ fn read_build_time(path: &Path) -> Result<Date> {
#[derive(Debug)]
pub struct GenerationLink {
pub version: u64,
pub label: Option<String>,
pub path: PathBuf,
pub build_time: Option<Date>,
}
Expand All @@ -201,6 +213,15 @@ impl GenerationLink {
pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
Ok(Self {
version: parse_version(&path).context("Failed to parse version")?,
label: None,
path: PathBuf::from(path.as_ref()),
build_time: read_build_time(path.as_ref()).ok(),
})
}
pub fn from_path_with_label(path: impl AsRef<Path>, label: String) -> Result<Self> {
Ok(Self {
version: 0,
label: Some(label),
path: PathBuf::from(path.as_ref()),
build_time: read_build_time(path.as_ref()).ok(),
})
Expand Down
5 changes: 5 additions & 0 deletions rust/tool/systemd/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ struct InstallCommand {

/// List of generation links (e.g. /nix/var/nix/profiles/system-*-link)
generations: Vec<PathBuf>,

/// A safe generation (usually /run/booted-system) that should always be kept, ignored from --configuration-limit if used
#[arg(long)]
safe_generation: Option<PathBuf>,
}

impl Cli {
Expand Down Expand Up @@ -113,6 +117,7 @@ fn install(args: InstallCommand) -> Result<()> {
args.bootcounting_initial_tries,
args.esp,
args.generations,
args.safe_generation,
);

if args.allow_unsigned
Expand Down
17 changes: 15 additions & 2 deletions rust/tool/systemd/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct InstallerBuilder {
bootcounting_initial_tries: u32,
esp: PathBuf,
generation_links: Vec<PathBuf>,
safe_generation: Option<PathBuf>,
}

impl InstallerBuilder {
Expand All @@ -46,6 +47,7 @@ impl InstallerBuilder {
bootcounting_initial_tries: u32,
esp: PathBuf,
generation_links: Vec<PathBuf>,
safe_generation: Option<PathBuf>,
) -> Self {
Self {
lanzaboote_stub: lanzaboote_stub.as_ref().to_path_buf(),
Expand All @@ -56,6 +58,7 @@ impl InstallerBuilder {
bootcounting_initial_tries,
esp,
generation_links,
safe_generation,
}
}

Expand All @@ -75,6 +78,7 @@ impl InstallerBuilder {
bootcounting_initial_tries: self.bootcounting_initial_tries,
esp_paths,
generation_links: self.generation_links,
safe_generation : self.safe_generation,
arch: self.arch,
}
}
Expand All @@ -91,6 +95,7 @@ pub struct Installer<S: Signer> {
bootcounting_initial_tries: u32,
esp_paths: SystemdEspPaths,
generation_links: Vec<PathBuf>,
safe_generation: Option<PathBuf>,
arch: Architecture,
}

Expand All @@ -104,6 +109,12 @@ impl<S: Signer> Installer<S> {
.map(GenerationLink::from_path)
.collect::<Result<Vec<GenerationLink>>>()?;

let mut all_links = self
.safe_generation
.iter()
.map(|p| GenerationLink::from_path_with_label(p, "SAFE".to_string()))
.collect::<Result<Vec<GenerationLink>>>()?;

// Sort the links by version, so that the limit actually skips the oldest generations.
links.sort_by_key(|l| l.version);

Expand All @@ -118,9 +129,11 @@ impl<S: Signer> Installer<S> {
.rev()
.take(self.configuration_limit)
.rev()
.collect()
.collect();
};
self.install_generations_from_links(&links)?;
all_links.append(&mut links);

self.install_generations_from_links(&all_links)?;

self.install_systemd_boot()?;

Expand Down