deb: make sure to have the right apt keyrings
Some checks failed
CI / build (push) Failing after 14s

This commit is contained in:
2026-01-20 19:31:07 +01:00
parent ab35af5fb5
commit dd9cc07285
5 changed files with 139 additions and 1 deletions

View File

@@ -10,6 +10,7 @@ dist_info:
dist: dist:
debian: debian:
base_url: http://deb.debian.org/debian base_url: http://deb.debian.org/debian
archive_keyring: https://ftp-master.debian.org/keys/archive-key-{series_num}.asc
pockets: pockets:
- proposed-updates - proposed-updates
- updates - updates
@@ -18,6 +19,7 @@ dist:
network: https://salsa.debian.org/debian/distro-info-data/-/raw/main/debian.csv network: https://salsa.debian.org/debian/distro-info-data/-/raw/main/debian.csv
ubuntu: ubuntu:
base_url: http://archive.ubuntu.com/ubuntu base_url: http://archive.ubuntu.com/ubuntu
archive_keyring: http://archive.ubuntu.com/ubuntu/project/ubuntu-archive-keyring.gpg
pockets: pockets:
- proposed - proposed
- updates - updates

View File

@@ -0,0 +1,54 @@
//! APT keyring management for mmdebstrap
//!
//! Provides a simple function to ensure that archive keyrings are available
//! for mmdebstrap operations by downloading them from specified URLs.
use crate::context;
use crate::distro_info;
use std::error::Error;
use std::path::Path;
use std::sync::Arc;
/// Download a keyring into apt trusted.gpg.d directory, trusting that keyring
pub async fn download_trust_keyring(
ctx: Option<Arc<context::Context>>,
series: &str,
) -> Result<(), Box<dyn Error>> {
let ctx = ctx.unwrap_or_else(context::current);
// Obtain keyring URL from distro_info
let keyring_url = distro_info::get_keyring_url(series).await?;
log::debug!("Downloading keyring from: {}", keyring_url);
// Create trusted.gpg.d directory if it doesn't exist
let trusted_gpg_d = "/etc/apt/trusted.gpg.d";
if !ctx.exists(Path::new(trusted_gpg_d))? {
ctx.command("mkdir").arg("-p").arg(trusted_gpg_d).status()?;
}
// Generate a filename for the keyring
let filename = format!("pkh-{}.gpg", series);
let keyring_path = format!("{}/{}", trusted_gpg_d, filename);
// Download the keyring directly to the final location using curl
let mut curl_cmd = ctx.command("curl");
curl_cmd
.arg("-s")
.arg("-f")
.arg("-L")
.arg(&keyring_url)
.arg("--output")
.arg(&keyring_path);
let status = curl_cmd.status()?;
if !status.success() {
return Err(format!("Failed to download keyring from {}", keyring_url).into());
}
log::info!(
"Successfully downloaded and installed keyring for {} to {}",
series,
keyring_path
);
Ok(())
}

View File

@@ -1 +1,2 @@
pub mod keyring;
pub mod sources; pub mod sources;

View File

@@ -120,6 +120,11 @@ impl EphemeralContextGuard {
.arg(lockfile_path.to_string_lossy().to_string()) .arg(lockfile_path.to_string_lossy().to_string())
.status()?; .status()?;
// Make sure we have the right apt keyrings to mmdebstrap the chroot
tokio::runtime::Runtime::new().unwrap().block_on(
crate::apt::keyring::download_trust_keyring(Some(ctx.clone()), series),
)?;
// Use mmdebstrap to download the tarball to the cache directory // Use mmdebstrap to download the tarball to the cache directory
let status = ctx let status = ctx
.command("mmdebstrap") .command("mmdebstrap")

View File

@@ -13,6 +13,7 @@ struct SeriesInfo {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct DistData { struct DistData {
base_url: String, base_url: String,
archive_keyring: String,
pockets: Vec<String>, pockets: Vec<String>,
series: SeriesInfo, series: SeriesInfo,
} }
@@ -124,6 +125,27 @@ pub fn get_base_url(dist: &str) -> String {
DATA.dist.get(dist).unwrap().base_url.clone() DATA.dist.get(dist).unwrap().base_url.clone()
} }
/// Obtain the URL for the archive keyring of a distribution series
pub async fn get_keyring_url(series: &str) -> Result<String, Box<dyn Error>> {
let dist = get_dist_from_series(series).await?;
let dist_data = DATA
.dist
.get(&dist)
.ok_or(format!("Unsupported distribution: {}", dist))?;
// For Debian, we need the series number to form the keyring URL
if dist == "debian" {
let series_num = get_debian_series_number(series).await?.unwrap();
// Replace {series_num} placeholder with the actual series number
Ok(dist_data
.archive_keyring
.replace("{series_num}", &series_num))
} else {
// For other distributions like Ubuntu, use the keyring directly
Ok(dist_data.archive_keyring.clone())
}
}
/// Obtain the URL for the 'Release' file of a distribution series /// Obtain the URL for the 'Release' file of a distribution series
fn get_release_url(base_url: &str, series: &str, pocket: &str) -> String { fn get_release_url(base_url: &str, series: &str, pocket: &str) -> String {
let pocket_full = if pocket.is_empty() { let pocket_full = if pocket.is_empty() {
@@ -159,6 +181,44 @@ pub async fn get_components(
Err("Components not found.".into()) Err("Components not found.".into())
} }
/// Map a Debian series name to its version number
pub async fn get_debian_series_number(series: &str) -> Result<Option<String>, Box<dyn Error>> {
let series_info = &DATA.dist.get("debian").unwrap().series;
let content = if Path::new(series_info.local.as_str()).exists() {
std::fs::read_to_string(series_info.local.as_str())?
} else {
reqwest::get(series_info.network.as_str())
.await?
.text()
.await?
};
let mut rdr = csv::ReaderBuilder::new()
.flexible(true)
.from_reader(content.as_bytes());
let headers = rdr.headers()?.clone();
let series_idx = headers
.iter()
.position(|h| h == "series")
.ok_or("Column 'series' not found")?;
let version_idx = headers
.iter()
.position(|h| h == "version")
.ok_or("Column 'version' not found")?;
for result in rdr.records() {
let record = result?;
if let (Some(s), Some(v)) = (record.get(series_idx), record.get(version_idx))
&& s.to_lowercase() == series.to_lowercase()
{
return Ok(Some(v.to_string()));
}
}
Ok(None)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -182,4 +242,20 @@ mod tests {
assert_eq!(get_dist_from_series("sid").await.unwrap(), "debian"); assert_eq!(get_dist_from_series("sid").await.unwrap(), "debian");
assert_eq!(get_dist_from_series("noble").await.unwrap(), "ubuntu"); assert_eq!(get_dist_from_series("noble").await.unwrap(), "ubuntu");
} }
#[tokio::test]
async fn test_get_debian_series_number() {
// Test with known Debian series
let bookworm_number = get_debian_series_number("bookworm").await.unwrap();
assert!(bookworm_number.is_some());
assert_eq!(bookworm_number.unwrap(), "12");
let trixie_number = get_debian_series_number("trixie").await.unwrap();
assert!(trixie_number.is_some());
assert_eq!(trixie_number.unwrap(), "13");
// Test with unknown series
let unknown_number = get_debian_series_number("unknown").await.unwrap();
assert!(unknown_number.is_none());
}
} }